1 /*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.swing;
27
28 import java.awt.*;
29 import java.awt.event.*;
30
31 import java.util.Vector;
32 import java.util.Locale;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.beans.Transient;
40
41 import javax.swing.event.*;
42 import javax.accessibility.*;
43 import javax.swing.plaf.*;
44 import javax.swing.text.Position;
45
46 import java.io.ObjectOutputStream;
47 import java.io.ObjectInputStream;
48 import java.io.IOException;
49 import java.io.Serializable;
50
51 import sun.swing.SwingUtilities2;
52 import sun.swing.SwingUtilities2.Section;
53 import static sun.swing.SwingUtilities2.Section.*;
54
55
56 /**
57 * A component that displays a list of objects and allows the user to select
58 * one or more items. A separate model, {@code ListModel}, maintains the
59 * contents of the list.
60 * <p>
61 * It's easy to display an array or Vector of objects, using the {@code JList}
62 * constructor that automatically builds a read-only {@code ListModel} instance
63 * for you:
64 * <pre>
65 * {@code
66 * // Create a JList that displays strings from an array
67 *
68 * String[] data = {"one", "two", "three", "four"};
69 * JList<String> myList = new JList<String>(data);
70 *
71 * // Create a JList that displays the superclasses of JList.class, by
72 * // creating it with a Vector populated with this data
73 *
74 * Vector<Class<?>> superClasses = new Vector<Class<?>>();
75 * Class<JList> rootClass = javax.swing.JList.class;
76 * for(Class<?> cls = rootClass; cls != null; cls = cls.getSuperclass()) {
77 * superClasses.addElement(cls);
78 * }
79 * JList<Class<?>> myList = new JList<Class<?>>(superClasses);
80 *
81 * // The automatically created model is stored in JList's "model"
82 * // property, which you can retrieve
83 *
84 * ListModel<Class<?>> model = myList.getModel();
85 * for(int i = 0; i < model.getSize(); i++) {
86 * System.out.println(model.getElementAt(i));
87 * }
88 * }
89 * </pre>
90 * <p>
91 * A {@code ListModel} can be supplied directly to a {@code JList} by way of a
92 * constructor or the {@code setModel} method. The contents need not be static -
93 * the number of items, and the values of items can change over time. A correct
94 * {@code ListModel} implementation notifies the set of
95 * {@code javax.swing.event.ListDataListener}s that have been added to it, each
96 * time a change occurs. These changes are characterized by a
97 * {@code javax.swing.event.ListDataEvent}, which identifies the range of list
98 * indices that have been modified, added, or removed. {@code JList}'s
99 * {@code ListUI} is responsible for keeping the visual representation up to
100 * date with changes, by listening to the model.
101 * <p>
102 * Simple, dynamic-content, {@code JList} applications can use the
103 * {@code DefaultListModel} class to maintain list elements. This class
104 * implements the {@code ListModel} interface and also provides a
105 * <code>java.util.Vector</code>-like API. Applications that need a more
106 * custom <code>ListModel</code> implementation may instead wish to subclass
107 * {@code AbstractListModel}, which provides basic support for managing and
108 * notifying listeners. For example, a read-only implementation of
109 * {@code AbstractListModel}:
110 * <pre>
111 * {@code
112 * // This list model has about 2^16 elements. Enjoy scrolling.
113 *
114 * ListModel<String> bigData = new AbstractListModel<String>() {
115 * public int getSize() { return Short.MAX_VALUE; }
116 * public String getElementAt(int index) { return "Index " + index; }
117 * };
118 * }
119 * </pre>
120 * <p>
121 * The selection state of a {@code JList} is managed by another separate
122 * model, an instance of {@code ListSelectionModel}. {@code JList} is
123 * initialized with a selection model on construction, and also contains
124 * methods to query or set this selection model. Additionally, {@code JList}
125 * provides convenient methods for easily managing the selection. These methods,
126 * such as {@code setSelectedIndex} and {@code getSelectedValue}, are cover
127 * methods that take care of the details of interacting with the selection
128 * model. By default, {@code JList}'s selection model is configured to allow any
129 * combination of items to be selected at a time; selection mode
130 * {@code MULTIPLE_INTERVAL_SELECTION}. The selection mode can be changed
131 * on the selection model directly, or via {@code JList}'s cover method.
132 * Responsibility for updating the selection model in response to user gestures
133 * lies with the list's {@code ListUI}.
134 * <p>
135 * A correct {@code ListSelectionModel} implementation notifies the set of
136 * {@code javax.swing.event.ListSelectionListener}s that have been added to it
137 * each time a change to the selection occurs. These changes are characterized
138 * by a {@code javax.swing.event.ListSelectionEvent}, which identifies the range
139 * of the selection change.
140 * <p>
141 * The preferred way to listen for changes in list selection is to add
142 * {@code ListSelectionListener}s directly to the {@code JList}. {@code JList}
143 * then takes care of listening to the the selection model and notifying your
144 * listeners of change.
145 * <p>
146 * Responsibility for listening to selection changes in order to keep the list's
147 * visual representation up to date lies with the list's {@code ListUI}.
148 * <p>
149 * <a name="renderer">
150 * Painting of cells in a {@code JList} is handled by a delegate called a
151 * cell renderer, installed on the list as the {@code cellRenderer} property.
152 * The renderer provides a {@code java.awt.Component} that is used
153 * like a "rubber stamp" to paint the cells. Each time a cell needs to be
154 * painted, the list's {@code ListUI} asks the cell renderer for the component,
155 * moves it into place, and has it paint the contents of the cell by way of its
156 * {@code paint} method. A default cell renderer, which uses a {@code JLabel}
157 * component to render, is installed by the lists's {@code ListUI}. You can
158 * substitute your own renderer using code like this:
159 * <pre>
160 * {@code
161 * // Display an icon and a string for each object in the list.
162 *
163 * class MyCellRenderer extends JLabel implements ListCellRenderer<Object> {
164 * final static ImageIcon longIcon = new ImageIcon("long.gif");
165 * final static ImageIcon shortIcon = new ImageIcon("short.gif");
166 *
167 * // This is the only method defined by ListCellRenderer.
168 * // We just reconfigure the JLabel each time we're called.
169 *
170 * public Component getListCellRendererComponent(
171 * JList<?> list, // the list
172 * Object value, // value to display
173 * int index, // cell index
174 * boolean isSelected, // is the cell selected
175 * boolean cellHasFocus) // does the cell have focus
176 * {
177 * String s = value.toString();
178 * setText(s);
179 * setIcon((s.length() > 10) ? longIcon : shortIcon);
180 * if (isSelected) {
181 * setBackground(list.getSelectionBackground());
182 * setForeground(list.getSelectionForeground());
183 * } else {
184 * setBackground(list.getBackground());
185 * setForeground(list.getForeground());
186 * }
187 * setEnabled(list.isEnabled());
188 * setFont(list.getFont());
189 * setOpaque(true);
190 * return this;
191 * }
192 * }
193 *
194 * myList.setCellRenderer(new MyCellRenderer());
195 * }
196 * </pre>
197 * <p>
198 * Another job for the cell renderer is in helping to determine sizing
199 * information for the list. By default, the list's {@code ListUI} determines
200 * the size of cells by asking the cell renderer for its preferred
201 * size for each list item. This can be expensive for large lists of items.
202 * To avoid these calculations, you can set a {@code fixedCellWidth} and
203 * {@code fixedCellHeight} on the list, or have these values calculated
204 * automatically based on a single prototype value:
205 * <a name="prototype_example">
206 * <pre>
207 * {@code
208 * JList<String> bigDataList = new JList<String>(bigData);
209 *
210 * // We don't want the JList implementation to compute the width
211 * // or height of all of the list cells, so we give it a string
212 * // that's as big as we'll need for any cell. It uses this to
213 * // compute values for the fixedCellWidth and fixedCellHeight
214 * // properties.
215 *
216 * bigDataList.setPrototypeCellValue("Index 1234567890");
217 * }
218 * </pre>
219 * <p>
220 * {@code JList} doesn't implement scrolling directly. To create a list that
221 * scrolls, make it the viewport view of a {@code JScrollPane}. For example:
222 * <pre>
223 * JScrollPane scrollPane = new JScrollPane(myList);
224 *
225 * // Or in two steps:
226 * JScrollPane scrollPane = new JScrollPane();
227 * scrollPane.getViewport().setView(myList);
228 * </pre>
229 * <p>
230 * {@code JList} doesn't provide any special handling of double or triple
231 * (or N) mouse clicks, but it's easy to add a {@code MouseListener} if you
232 * wish to take action on these events. Use the {@code locationToIndex}
233 * method to determine what cell was clicked. For example:
234 * <pre>
235 * MouseListener mouseListener = new MouseAdapter() {
236 * public void mouseClicked(MouseEvent e) {
237 * if (e.getClickCount() == 2) {
238 * int index = list.locationToIndex(e.getPoint());
239 * System.out.println("Double clicked on Item " + index);
240 * }
241 * }
242 * };
243 * list.addMouseListener(mouseListener);
244 * </pre>
245 * <p>
246 * <strong>Warning:</strong> Swing is not thread safe. For more
247 * information see <a
248 * href="package-summary.html#threading">Swing's Threading
249 * Policy</a>.
250 * <p>
251 * <strong>Warning:</strong>
252 * Serialized objects of this class will not be compatible with
253 * future Swing releases. The current serialization support is
254 * appropriate for short term storage or RMI between applications running
255 * the same version of Swing. As of 1.4, support for long term storage
256 * of all JavaBeans<sup><font size="-2">TM</font></sup>
257 * has been added to the <code>java.beans</code> package.
258 * Please see {@link java.beans.XMLEncoder}.
259 * <p>
260 * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/list.html">How to Use Lists</a>
261 * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
262 * for further documentation.
263 * Also see the article <a href="http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html">Advanced JList Programming</a>
264 * in <a href="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
265 * <p>
266 * @see ListModel
267 * @see AbstractListModel
268 * @see DefaultListModel
269 * @see ListSelectionModel
270 * @see DefaultListSelectionModel
271 * @see ListCellRenderer
272 * @see DefaultListCellRenderer
273 *
274 * @param <E> the type of the elements of this list
275 *
276 * @beaninfo
277 * attribute: isContainer false
278 * description: A component which allows for the selection of one or more objects from a list.
279 *
280 * @author Hans Muller
281 */
282 public class JList<E> extends JComponent implements Scrollable, Accessible
283 {
284 /**
285 * @see #getUIClassID
286 * @see #readObject
287 */
288 private static final String uiClassID = "ListUI";
289
290 /**
291 * Indicates a vertical layout of cells, in a single column;
292 * the default layout.
293 * @see #setLayoutOrientation
294 * @since 1.4
295 */
296 public static final int VERTICAL = 0;
297
298 /**
299 * Indicates a "newspaper style" layout with cells flowing vertically
300 * then horizontally.
301 * @see #setLayoutOrientation
302 * @since 1.4
303 */
304 public static final int VERTICAL_WRAP = 1;
305
306 /**
307 * Indicates a "newspaper style" layout with cells flowing horizontally
308 * then vertically.
309 * @see #setLayoutOrientation
310 * @since 1.4
311 */
312 public static final int HORIZONTAL_WRAP = 2;
313
314 private int fixedCellWidth = -1;
315 private int fixedCellHeight = -1;
316 private int horizontalScrollIncrement = -1;
317 private E prototypeCellValue;
318 private int visibleRowCount = 8;
319 private Color selectionForeground;
320 private Color selectionBackground;
321 private boolean dragEnabled;
322
323 private ListSelectionModel selectionModel;
324 private ListModel<E> dataModel;
325 private ListCellRenderer<? super E> cellRenderer;
326 private ListSelectionListener selectionListener;
327
328 /**
329 * How to lay out the cells; defaults to <code>VERTICAL</code>.
330 */
331 private int layoutOrientation;
332
333 /**
334 * The drop mode for this component.
335 */
336 private DropMode dropMode = DropMode.USE_SELECTION;
337
338 /**
339 * The drop location.
340 */
341 private transient DropLocation dropLocation;
342
343 /**
344 * A subclass of <code>TransferHandler.DropLocation</code> representing
345 * a drop location for a <code>JList</code>.
346 *
347 * @see #getDropLocation
348 * @since 1.6
349 */
350 public static final class DropLocation extends TransferHandler.DropLocation {
351 private final int index;
352 private final boolean isInsert;
353
354 private DropLocation(Point p, int index, boolean isInsert) {
355 super(p);
356 this.index = index;
357 this.isInsert = isInsert;
358 }
359
360 /**
361 * Returns the index where dropped data should be placed in the
362 * list. Interpretation of the value depends on the drop mode set on
363 * the associated component. If the drop mode is either
364 * <code>DropMode.USE_SELECTION</code> or <code>DropMode.ON</code>,
365 * the return value is an index of a row in the list. If the drop mode is
366 * <code>DropMode.INSERT</code>, the return value refers to the index
367 * where the data should be inserted. If the drop mode is
368 * <code>DropMode.ON_OR_INSERT</code>, the value of
369 * <code>isInsert()</code> indicates whether the index is an index
370 * of a row, or an insert index.
371 * <p>
372 * <code>-1</code> indicates that the drop occurred over empty space,
373 * and no index could be calculated.
374 *
375 * @return the drop index
376 */
377 public int getIndex() {
378 return index;
379 }
380
381 /**
382 * Returns whether or not this location represents an insert
383 * location.
384 *
385 * @return whether or not this is an insert location
386 */
387 public boolean isInsert() {
388 return isInsert;
389 }
390
391 /**
392 * Returns a string representation of this drop location.
393 * This method is intended to be used for debugging purposes,
394 * and the content and format of the returned string may vary
395 * between implementations.
396 *
397 * @return a string representation of this drop location
398 */
399 public String toString() {
400 return getClass().getName()
401 + "[dropPoint=" + getDropPoint() + ","
402 + "index=" + index + ","
403 + "insert=" + isInsert + "]";
404 }
405 }
406
407 /**
408 * Constructs a {@code JList} that displays elements from the specified,
409 * {@code non-null}, model. All {@code JList} constructors delegate to
410 * this one.
411 * <p>
412 * This constructor registers the list with the {@code ToolTipManager},
413 * allowing for tooltips to be provided by the cell renderers.
414 *
415 * @param dataModel the model for the list
416 * @exception IllegalArgumentException if the model is {@code null}
417 */
418 public JList(ListModel<E> dataModel)
419 {
420 if (dataModel == null) {
421 throw new IllegalArgumentException("dataModel must be non null");
422 }
423
424 // Register with the ToolTipManager so that tooltips from the
425 // renderer show through.
426 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
427 toolTipManager.registerComponent(this);
428
429 layoutOrientation = VERTICAL;
430
431 this.dataModel = dataModel;
432 selectionModel = createSelectionModel();
433 setAutoscrolls(true);
434 setOpaque(true);
435 updateUI();
436 }
437
438
439 /**
440 * Constructs a <code>JList</code> that displays the elements in
441 * the specified array. This constructor creates a read-only model
442 * for the given array, and then delegates to the constructor that
443 * takes a {@code ListModel}.
444 * <p>
445 * Attempts to pass a {@code null} value to this method results in
446 * undefined behavior and, most likely, exceptions. The created model
447 * references the given array directly. Attempts to modify the array
448 * after constructing the list results in undefined behavior.
449 *
450 * @param listData the array of Objects to be loaded into the data model,
451 * {@code non-null}
452 */
453 public JList(final E[] listData)
454 {
455 this (
456 new AbstractListModel<E>() {
457 public int getSize() { return listData.length; }
458 public E getElementAt(int i) { return listData[i]; }
459 }
460 );
461 }
462
463
464 /**
465 * Constructs a <code>JList</code> that displays the elements in
466 * the specified <code>Vector</code>. This constructor creates a read-only
467 * model for the given {@code Vector}, and then delegates to the constructor
468 * that takes a {@code ListModel}.
469 * <p>
470 * Attempts to pass a {@code null} value to this method results in
471 * undefined behavior and, most likely, exceptions. The created model
472 * references the given {@code Vector} directly. Attempts to modify the
473 * {@code Vector} after constructing the list results in undefined behavior.
474 *
475 * @param listData the <code>Vector</code> to be loaded into the
476 * data model, {@code non-null}
477 */
478 public JList(final Vector<? extends E> listData) {
479 this (
480 new AbstractListModel<E>() {
481 public int getSize() { return listData.size(); }
482 public E getElementAt(int i) { return listData.elementAt(i); }
483 }
484 );
485 }
486
487
488 /**
489 * Constructs a <code>JList</code> with an empty, read-only, model.
490 */
491 public JList() {
492 this (
493 new AbstractListModel<E>() {
494 public int getSize() { return 0; }
495 public E getElementAt(int i) { throw new IndexOutOfBoundsException("No Data Model"); }
496 }
497 );
498 }
499
500
501 /**
502 * Returns the {@code ListUI}, the look and feel object that
503 * renders this component.
504 *
505 * @return the <code>ListUI</code> object that renders this component
506 */
507 public ListUI getUI() {
508 return (ListUI)ui;
509 }
510
511
512 /**
513 * Sets the {@code ListUI}, the look and feel object that
514 * renders this component.
515 *
516 * @param ui the <code>ListUI</code> object
517 * @see UIDefaults#getUI
518 * @beaninfo
519 * bound: true
520 * hidden: true
521 * attribute: visualUpdate true
522 * description: The UI object that implements the Component's LookAndFeel.
523 */
524 public void setUI(ListUI ui) {
525 super.setUI(ui);
526 }
527
528
529 /**
530 * Resets the {@code ListUI} property by setting it to the value provided
531 * by the current look and feel. If the current cell renderer was installed
532 * by the developer (rather than the look and feel itself), this also causes
533 * the cell renderer and its children to be updated, by calling
534 * {@code SwingUtilities.updateComponentTreeUI} on it.
535 *
536 * @see UIManager#getUI
537 * @see SwingUtilities#updateComponentTreeUI
538 */
539 public void updateUI() {
540 setUI((ListUI)UIManager.getUI(this));
541
542 ListCellRenderer<? super E> renderer = getCellRenderer();
543 if (renderer instanceof Component) {
544 SwingUtilities.updateComponentTreeUI((Component)renderer);
545 }
546 }
547
548
549 /**
550 * Returns {@code "ListUI"}, the <code>UIDefaults</code> key used to look
551 * up the name of the {@code javax.swing.plaf.ListUI} class that defines
552 * the look and feel for this component.
553 *
554 * @return the string "ListUI"
555 * @see JComponent#getUIClassID
556 * @see UIDefaults#getUI
557 */
558 public String getUIClassID() {
559 return uiClassID;
560 }
561
562
563 /* -----private-----
564 * This method is called by setPrototypeCellValue and setCellRenderer
565 * to update the fixedCellWidth and fixedCellHeight properties from the
566 * current value of prototypeCellValue (if it's non null).
567 * <p>
568 * This method sets fixedCellWidth and fixedCellHeight but does <b>not</b>
569 * generate PropertyChangeEvents for them.
570 *
571 * @see #setPrototypeCellValue
572 * @see #setCellRenderer
573 */
574 private void updateFixedCellSize()
575 {
576 ListCellRenderer<? super E> cr = getCellRenderer();
577 E value = getPrototypeCellValue();
578
579 if ((cr != null) && (value != null)) {
580 Component c = cr.getListCellRendererComponent(this, value, 0, false, false);
581
582 /* The ListUI implementation will add Component c to its private
583 * CellRendererPane however we can't assume that's already
584 * been done here. So we temporarily set the one "inherited"
585 * property that may affect the renderer components preferred size:
586 * its font.
587 */
588 Font f = c.getFont();
589 c.setFont(getFont());
590
591 Dimension d = c.getPreferredSize();
592 fixedCellWidth = d.width;
593 fixedCellHeight = d.height;
594
595 c.setFont(f);
596 }
597 }
598
599
600 /**
601 * Returns the "prototypical" cell value -- a value used to calculate a
602 * fixed width and height for cells. This can be {@code null} if there
603 * is no such value.
604 *
605 * @return the value of the {@code prototypeCellValue} property
606 * @see #setPrototypeCellValue
607 */
608 public E getPrototypeCellValue() {
609 return prototypeCellValue;
610 }
611
612 /**
613 * Sets the {@code prototypeCellValue} property, and then (if the new value
614 * is {@code non-null}), computes the {@code fixedCellWidth} and
615 * {@code fixedCellHeight} properties by requesting the cell renderer
616 * component for the given value (and index 0) from the cell renderer, and
617 * using that component's preferred size.
618 * <p>
619 * This method is useful when the list is too long to allow the
620 * {@code ListUI} to compute the width/height of each cell, and there is a
621 * single cell value that is known to occupy as much space as any of the
622 * others, a so-called prototype.
623 * <p>
624 * While all three of the {@code prototypeCellValue},
625 * {@code fixedCellHeight}, and {@code fixedCellWidth} properties may be
626 * modified by this method, {@code PropertyChangeEvent} notifications are
627 * only sent when the {@code prototypeCellValue} property changes.
628 * <p>
629 * To see an example which sets this property, see the
630 * <a href="#prototype_example">class description</a> above.
631 * <p>
632 * The default value of this property is <code>null</code>.
633 * <p>
634 * This is a JavaBeans bound property.
635 *
636 * @param prototypeCellValue the value on which to base
637 * <code>fixedCellWidth</code> and
638 * <code>fixedCellHeight</code>
639 * @see #getPrototypeCellValue
640 * @see #setFixedCellWidth
641 * @see #setFixedCellHeight
642 * @see JComponent#addPropertyChangeListener
643 * @beaninfo
644 * bound: true
645 * attribute: visualUpdate true
646 * description: The cell prototype value, used to compute cell width and height.
647 */
648 public void setPrototypeCellValue(E prototypeCellValue) {
649 E oldValue = this.prototypeCellValue;
650 this.prototypeCellValue = prototypeCellValue;
651
652 /* If the prototypeCellValue has changed and is non-null,
653 * then recompute fixedCellWidth and fixedCellHeight.
654 */
655
656 if ((prototypeCellValue != null) && !prototypeCellValue.equals(oldValue)) {
657 updateFixedCellSize();
658 }
659
660 firePropertyChange("prototypeCellValue", oldValue, prototypeCellValue);
661 }
662
663
664 /**
665 * Returns the value of the {@code fixedCellWidth} property.
666 *
667 * @return the fixed cell width
668 * @see #setFixedCellWidth
669 */
670 public int getFixedCellWidth() {
671 return fixedCellWidth;
672 }
673
674 /**
675 * Sets a fixed value to be used for the width of every cell in the list.
676 * If {@code width} is -1, cell widths are computed in the {@code ListUI}
677 * by applying <code>getPreferredSize</code> to the cell renderer component
678 * for each list element.
679 * <p>
680 * The default value of this property is {@code -1}.
681 * <p>
682 * This is a JavaBeans bound property.
683 *
684 * @param width the width to be used for all cells in the list
685 * @see #setPrototypeCellValue
686 * @see #setFixedCellWidth
687 * @see JComponent#addPropertyChangeListener
688 * @beaninfo
689 * bound: true
690 * attribute: visualUpdate true
691 * description: Defines a fixed cell width when greater than zero.
692 */
693 public void setFixedCellWidth(int width) {
694 int oldValue = fixedCellWidth;
695 fixedCellWidth = width;
696 firePropertyChange("fixedCellWidth", oldValue, fixedCellWidth);
697 }
698
699
700 /**
701 * Returns the value of the {@code fixedCellHeight} property.
702 *
703 * @return the fixed cell height
704 * @see #setFixedCellHeight
705 */
706 public int getFixedCellHeight() {
707 return fixedCellHeight;
708 }
709
710 /**
711 * Sets a fixed value to be used for the height of every cell in the list.
712 * If {@code height} is -1, cell heights are computed in the {@code ListUI}
713 * by applying <code>getPreferredSize</code> to the cell renderer component
714 * for each list element.
715 * <p>
716 * The default value of this property is {@code -1}.
717 * <p>
718 * This is a JavaBeans bound property.
719 *
720 * @param height the height to be used for for all cells in the list
721 * @see #setPrototypeCellValue
722 * @see #setFixedCellWidth
723 * @see JComponent#addPropertyChangeListener
724 * @beaninfo
725 * bound: true
726 * attribute: visualUpdate true
727 * description: Defines a fixed cell height when greater than zero.
728 */
729 public void setFixedCellHeight(int height) {
730 int oldValue = fixedCellHeight;
731 fixedCellHeight = height;
732 firePropertyChange("fixedCellHeight", oldValue, fixedCellHeight);
733 }
734
735
736 /**
737 * Returns the object responsible for painting list items.
738 *
739 * @return the value of the {@code cellRenderer} property
740 * @see #setCellRenderer
741 */
742 @Transient
743 public ListCellRenderer<? super E> getCellRenderer() {
744 return cellRenderer;
745 }
746
747 /**
748 * Sets the delegate that is used to paint each cell in the list.
749 * The job of a cell renderer is discussed in detail in the
750 * <a href="#renderer">class level documentation</a>.
751 * <p>
752 * If the {@code prototypeCellValue} property is {@code non-null},
753 * setting the cell renderer also causes the {@code fixedCellWidth} and
754 * {@code fixedCellHeight} properties to be re-calculated. Only one
755 * <code>PropertyChangeEvent</code> is generated however -
756 * for the <code>cellRenderer</code> property.
757 * <p>
758 * The default value of this property is provided by the {@code ListUI}
759 * delegate, i.e. by the look and feel implementation.
760 * <p>
761 * This is a JavaBeans bound property.
762 *
763 * @param cellRenderer the <code>ListCellRenderer</code>
764 * that paints list cells
765 * @see #getCellRenderer
766 * @beaninfo
767 * bound: true
768 * attribute: visualUpdate true
769 * description: The component used to draw the cells.
770 */
771 public void setCellRenderer(ListCellRenderer<? super E> cellRenderer) {
772 ListCellRenderer<? super E> oldValue = this.cellRenderer;
773 this.cellRenderer = cellRenderer;
774
775 /* If the cellRenderer has changed and prototypeCellValue
776 * was set, then recompute fixedCellWidth and fixedCellHeight.
777 */
778 if ((cellRenderer != null) && !cellRenderer.equals(oldValue)) {
779 updateFixedCellSize();
780 }
781
782 firePropertyChange("cellRenderer", oldValue, cellRenderer);
783 }
784
785
786 /**
787 * Returns the color used to draw the foreground of selected items.
788 * {@code DefaultListCellRenderer} uses this color to draw the foreground
789 * of items in the selected state, as do the renderers installed by most
790 * {@code ListUI} implementations.
791 *
792 * @return the color to draw the foreground of selected items
793 * @see #setSelectionForeground
794 * @see DefaultListCellRenderer
795 */
796 public Color getSelectionForeground() {
797 return selectionForeground;
798 }
799
800
801 /**
802 * Sets the color used to draw the foreground of selected items, which
803 * cell renderers can use to render text and graphics.
804 * {@code DefaultListCellRenderer} uses this color to draw the foreground
805 * of items in the selected state, as do the renderers installed by most
806 * {@code ListUI} implementations.
807 * <p>
808 * The default value of this property is defined by the look and feel
809 * implementation.
810 * <p>
811 * This is a JavaBeans bound property.
812 *
813 * @param selectionForeground the {@code Color} to use in the foreground
814 * for selected list items
815 * @see #getSelectionForeground
816 * @see #setSelectionBackground
817 * @see #setForeground
818 * @see #setBackground
819 * @see #setFont
820 * @see DefaultListCellRenderer
821 * @beaninfo
822 * bound: true
823 * attribute: visualUpdate true
824 * description: The foreground color of selected cells.
825 */
826 public void setSelectionForeground(Color selectionForeground) {
827 Color oldValue = this.selectionForeground;
828 this.selectionForeground = selectionForeground;
829 firePropertyChange("selectionForeground", oldValue, selectionForeground);
830 }
831
832
833 /**
834 * Returns the color used to draw the background of selected items.
835 * {@code DefaultListCellRenderer} uses this color to draw the background
836 * of items in the selected state, as do the renderers installed by most
837 * {@code ListUI} implementations.
838 *
839 * @return the color to draw the background of selected items
840 * @see #setSelectionBackground
841 * @see DefaultListCellRenderer
842 */
843 public Color getSelectionBackground() {
844 return selectionBackground;
845 }
846
847
848 /**
849 * Sets the color used to draw the background of selected items, which
850 * cell renderers can use fill selected cells.
851 * {@code DefaultListCellRenderer} uses this color to fill the background
852 * of items in the selected state, as do the renderers installed by most
853 * {@code ListUI} implementations.
854 * <p>
855 * The default value of this property is defined by the look
856 * and feel implementation.
857 * <p>
858 * This is a JavaBeans bound property.
859 *
860 * @param selectionBackground the {@code Color} to use for the
861 * background of selected cells
862 * @see #getSelectionBackground
863 * @see #setSelectionForeground
864 * @see #setForeground
865 * @see #setBackground
866 * @see #setFont
867 * @see DefaultListCellRenderer
868 * @beaninfo
869 * bound: true
870 * attribute: visualUpdate true
871 * description: The background color of selected cells.
872 */
873 public void setSelectionBackground(Color selectionBackground) {
874 Color oldValue = this.selectionBackground;
875 this.selectionBackground = selectionBackground;
876 firePropertyChange("selectionBackground", oldValue, selectionBackground);
877 }
878
879
880 /**
881 * Returns the value of the {@code visibleRowCount} property. See the
882 * documentation for {@link #setVisibleRowCount} for details on how to
883 * interpret this value.
884 *
885 * @return the value of the {@code visibleRowCount} property.
886 * @see #setVisibleRowCount
887 */
888 public int getVisibleRowCount() {
889 return visibleRowCount;
890 }
891
892 /**
893 * Sets the {@code visibleRowCount} property, which has different meanings
894 * depending on the layout orientation: For a {@code VERTICAL} layout
895 * orientation, this sets the preferred number of rows to display without
896 * requiring scrolling; for other orientations, it affects the wrapping of
897 * cells.
898 * <p>
899 * In {@code VERTICAL} orientation:<br>
900 * Setting this property affects the return value of the
901 * {@link #getPreferredScrollableViewportSize} method, which is used to
902 * calculate the preferred size of an enclosing viewport. See that method's
903 * documentation for more details.
904 * <p>
905 * In {@code HORIZONTAL_WRAP} and {@code VERTICAL_WRAP} orientations:<br>
906 * This affects how cells are wrapped. See the documentation of
907 * {@link #setLayoutOrientation} for more details.
908 * <p>
909 * The default value of this property is {@code 8}.
910 * <p>
911 * Calling this method with a negative value results in the property
912 * being set to {@code 0}.
913 * <p>
914 * This is a JavaBeans bound property.
915 *
916 * @param visibleRowCount an integer specifying the preferred number of
917 * rows to display without requiring scrolling
918 * @see #getVisibleRowCount
919 * @see #getPreferredScrollableViewportSize
920 * @see #setLayoutOrientation
921 * @see JComponent#getVisibleRect
922 * @see JViewport
923 * @beaninfo
924 * bound: true
925 * attribute: visualUpdate true
926 * description: The preferred number of rows to display without
927 * requiring scrolling
928 */
929 public void setVisibleRowCount(int visibleRowCount) {
930 int oldValue = this.visibleRowCount;
931 this.visibleRowCount = Math.max(0, visibleRowCount);
932 firePropertyChange("visibleRowCount", oldValue, visibleRowCount);
933 }
934
935
936 /**
937 * Returns the layout orientation property for the list: {@code VERTICAL}
938 * if the layout is a single column of cells, {@code VERTICAL_WRAP} if the
939 * layout is "newspaper style" with the content flowing vertically then
940 * horizontally, or {@code HORIZONTAL_WRAP} if the layout is "newspaper
941 * style" with the content flowing horizontally then vertically.
942 *
943 * @return the value of the {@code layoutOrientation} property
944 * @see #setLayoutOrientation
945 * @since 1.4
946 */
947 public int getLayoutOrientation() {
948 return layoutOrientation;
949 }
950
951
952 /**
953 * Defines the way list cells are layed out. Consider a {@code JList}
954 * with five cells. Cells can be layed out in one of the following ways:
955 * <p>
956 * <pre>
957 * VERTICAL: 0
958 * 1
959 * 2
960 * 3
961 * 4
962 *
963 * HORIZONTAL_WRAP: 0 1 2
964 * 3 4
965 *
966 * VERTICAL_WRAP: 0 3
967 * 1 4
968 * 2
969 * </pre>
970 * <p>
971 * A description of these layouts follows:
972 *
973 * <table border="1"
974 * summary="Describes layouts VERTICAL, HORIZONTAL_WRAP, and VERTICAL_WRAP">
975 * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr>
976 * <tr><td><code>VERTICAL</code>
977 * <td>Cells are layed out vertically in a single column.
978 * <tr><td><code>HORIZONTAL_WRAP</code>
979 * <td>Cells are layed out horizontally, wrapping to a new row as
980 * necessary. If the {@code visibleRowCount} property is less than
981 * or equal to zero, wrapping is determined by the width of the
982 * list; otherwise wrapping is done in such a way as to ensure
983 * {@code visibleRowCount} rows in the list.
984 * <tr><td><code>VERTICAL_WRAP</code>
985 * <td>Cells are layed out vertically, wrapping to a new column as
986 * necessary. If the {@code visibleRowCount} property is less than
987 * or equal to zero, wrapping is determined by the height of the
988 * list; otherwise wrapping is done at {@code visibleRowCount} rows.
989 * </table>
990 * <p>
991 * The default value of this property is <code>VERTICAL</code>.
992 *
993 * @param layoutOrientation the new layout orientation, one of:
994 * {@code VERTICAL}, {@code HORIZONTAL_WRAP} or {@code VERTICAL_WRAP}
995 * @see #getLayoutOrientation
996 * @see #setVisibleRowCount
997 * @see #getScrollableTracksViewportHeight
998 * @see #getScrollableTracksViewportWidth
999 * @throws IllegalArgumentException if {@code layoutOrientation} isn't one of the
1000 * allowable values
1001 * @since 1.4
1002 * @beaninfo
1003 * bound: true
1004 * attribute: visualUpdate true
1005 * description: Defines the way list cells are layed out.
1006 * enum: VERTICAL JList.VERTICAL
1007 * HORIZONTAL_WRAP JList.HORIZONTAL_WRAP
1008 * VERTICAL_WRAP JList.VERTICAL_WRAP
1009 */
1010 public void setLayoutOrientation(int layoutOrientation) {
1011 int oldValue = this.layoutOrientation;
1012 switch (layoutOrientation) {
1013 case VERTICAL:
1014 case VERTICAL_WRAP:
1015 case HORIZONTAL_WRAP:
1016 this.layoutOrientation = layoutOrientation;
1017 firePropertyChange("layoutOrientation", oldValue, layoutOrientation);
1018 break;
1019 default:
1020 throw new IllegalArgumentException("layoutOrientation must be one of: VERTICAL, HORIZONTAL_WRAP or VERTICAL_WRAP");
1021 }
1022 }
1023
1024
1025 /**
1026 * Returns the smallest list index that is currently visible.
1027 * In a left-to-right {@code componentOrientation}, the first visible
1028 * cell is found closest to the list's upper-left corner. In right-to-left
1029 * orientation, it is found closest to the upper-right corner.
1030 * If nothing is visible or the list is empty, {@code -1} is returned.
1031 * Note that the returned cell may only be partially visible.
1032 *
1033 * @return the index of the first visible cell
1034 * @see #getLastVisibleIndex
1035 * @see JComponent#getVisibleRect
1036 */
1037 public int getFirstVisibleIndex() {
1038 Rectangle r = getVisibleRect();
1039 int first;
1040 if (this.getComponentOrientation().isLeftToRight()) {
1041 first = locationToIndex(r.getLocation());
1042 } else {
1043 first = locationToIndex(new Point((r.x + r.width) - 1, r.y));
1044 }
1045 if (first != -1) {
1046 Rectangle bounds = getCellBounds(first, first);
1047 if (bounds != null) {
1048 SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, bounds);
1049 if (bounds.width == 0 || bounds.height == 0) {
1050 first = -1;
1051 }
1052 }
1053 }
1054 return first;
1055 }
1056
1057
1058 /**
1059 * Returns the largest list index that is currently visible.
1060 * If nothing is visible or the list is empty, {@code -1} is returned.
1061 * Note that the returned cell may only be partially visible.
1062 *
1063 * @return the index of the last visible cell
1064 * @see #getFirstVisibleIndex
1065 * @see JComponent#getVisibleRect
1066 */
1067 public int getLastVisibleIndex() {
1068 boolean leftToRight = this.getComponentOrientation().isLeftToRight();
1069 Rectangle r = getVisibleRect();
1070 Point lastPoint;
1071 if (leftToRight) {
1072 lastPoint = new Point((r.x + r.width) - 1, (r.y + r.height) - 1);
1073 } else {
1074 lastPoint = new Point(r.x, (r.y + r.height) - 1);
1075 }
1076 int location = locationToIndex(lastPoint);
1077
1078 if (location != -1) {
1079 Rectangle bounds = getCellBounds(location, location);
1080
1081 if (bounds != null) {
1082 SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, bounds);
1083 if (bounds.width == 0 || bounds.height == 0) {
1084 // Try the top left(LTR) or top right(RTL) corner, and
1085 // then go across checking each cell for HORIZONTAL_WRAP.
1086 // Try the lower left corner, and then go across checking
1087 // each cell for other list layout orientation.
1088 boolean isHorizontalWrap =
1089 (getLayoutOrientation() == HORIZONTAL_WRAP);
1090 Point visibleLocation = isHorizontalWrap ?
1091 new Point(lastPoint.x, r.y) :
1092 new Point(r.x, lastPoint.y);
1093 int last;
1094 int visIndex = -1;
1095 int lIndex = location;
1096 location = -1;
1097
1098 do {
1099 last = visIndex;
1100 visIndex = locationToIndex(visibleLocation);
1101
1102 if (visIndex != -1) {
1103 bounds = getCellBounds(visIndex, visIndex);
1104 if (visIndex != lIndex && bounds != null &&
1105 bounds.contains(visibleLocation)) {
1106 location = visIndex;
1107 if (isHorizontalWrap) {
1108 visibleLocation.y = bounds.y + bounds.height;
1109 if (visibleLocation.y >= lastPoint.y) {
1110 // Past visible region, bail.
1111 last = visIndex;
1112 }
1113 }
1114 else {
1115 visibleLocation.x = bounds.x + bounds.width;
1116 if (visibleLocation.x >= lastPoint.x) {
1117 // Past visible region, bail.
1118 last = visIndex;
1119 }
1120 }
1121
1122 }
1123 else {
1124 last = visIndex;
1125 }
1126 }
1127 } while (visIndex != -1 && last != visIndex);
1128 }
1129 }
1130 }
1131 return location;
1132 }
1133
1134
1135 /**
1136 * Scrolls the list within an enclosing viewport to make the specified
1137 * cell completely visible. This calls {@code scrollRectToVisible} with
1138 * the bounds of the specified cell. For this method to work, the
1139 * {@code JList} must be within a <code>JViewport</code>.
1140 * <p>
1141 * If the given index is outside the list's range of cells, this method
1142 * results in nothing.
1143 *
1144 * @param index the index of the cell to make visible
1145 * @see JComponent#scrollRectToVisible
1146 * @see #getVisibleRect
1147 */
1148 public void ensureIndexIsVisible(int index) {
1149 Rectangle cellBounds = getCellBounds(index, index);
1150 if (cellBounds != null) {
1151 scrollRectToVisible(cellBounds);
1152 }
1153 }
1154
1155 /**
1156 * Turns on or off automatic drag handling. In order to enable automatic
1157 * drag handling, this property should be set to {@code true}, and the
1158 * list's {@code TransferHandler} needs to be {@code non-null}.
1159 * The default value of the {@code dragEnabled} property is {@code false}.
1160 * <p>
1161 * The job of honoring this property, and recognizing a user drag gesture,
1162 * lies with the look and feel implementation, and in particular, the list's
1163 * {@code ListUI}. When automatic drag handling is enabled, most look and
1164 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1165 * drag and drop operation whenever the user presses the mouse button over
1166 * an item and then moves the mouse a few pixels. Setting this property to
1167 * {@code true} can therefore have a subtle effect on how selections behave.
1168 * <p>
1169 * If a look and feel is used that ignores this property, you can still
1170 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1171 * list's {@code TransferHandler}.
1172 *
1173 * @param b whether or not to enable automatic drag handling
1174 * @exception HeadlessException if
1175 * <code>b</code> is <code>true</code> and
1176 * <code>GraphicsEnvironment.isHeadless()</code>
1177 * returns <code>true</code>
1178 * @see java.awt.GraphicsEnvironment#isHeadless
1179 * @see #getDragEnabled
1180 * @see #setTransferHandler
1181 * @see TransferHandler
1182 * @since 1.4
1183 *
1184 * @beaninfo
1185 * description: determines whether automatic drag handling is enabled
1186 * bound: false
1187 */
1188 public void setDragEnabled(boolean b) {
1189 if (b && GraphicsEnvironment.isHeadless()) {
1190 throw new HeadlessException();
1191 }
1192 dragEnabled = b;
1193 }
1194
1195 /**
1196 * Returns whether or not automatic drag handling is enabled.
1197 *
1198 * @return the value of the {@code dragEnabled} property
1199 * @see #setDragEnabled
1200 * @since 1.4
1201 */
1202 public boolean getDragEnabled() {
1203 return dragEnabled;
1204 }
1205
1206 /**
1207 * Sets the drop mode for this component. For backward compatibility,
1208 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1209 * Usage of one of the other modes is recommended, however, for an
1210 * improved user experience. <code>DropMode.ON</code>, for instance,
1211 * offers similar behavior of showing items as selected, but does so without
1212 * affecting the actual selection in the list.
1213 * <p>
1214 * <code>JList</code> supports the following drop modes:
1215 * <ul>
1216 * <li><code>DropMode.USE_SELECTION</code></li>
1217 * <li><code>DropMode.ON</code></li>
1218 * <li><code>DropMode.INSERT</code></li>
1219 * <li><code>DropMode.ON_OR_INSERT</code></li>
1220 * </ul>
1221 * The drop mode is only meaningful if this component has a
1222 * <code>TransferHandler</code> that accepts drops.
1223 *
1224 * @param dropMode the drop mode to use
1225 * @throws IllegalArgumentException if the drop mode is unsupported
1226 * or <code>null</code>
1227 * @see #getDropMode
1228 * @see #getDropLocation
1229 * @see #setTransferHandler
1230 * @see TransferHandler
1231 * @since 1.6
1232 */
1233 public final void setDropMode(DropMode dropMode) {
1234 if (dropMode != null) {
1235 switch (dropMode) {
1236 case USE_SELECTION:
1237 case ON:
1238 case INSERT:
1239 case ON_OR_INSERT:
1240 this.dropMode = dropMode;
1241 return;
1242 }
1243 }
1244
1245 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for list");
1246 }
1247
1248 /**
1249 * Returns the drop mode for this component.
1250 *
1251 * @return the drop mode for this component
1252 * @see #setDropMode
1253 * @since 1.6
1254 */
1255 public final DropMode getDropMode() {
1256 return dropMode;
1257 }
1258
1259 /**
1260 * Calculates a drop location in this component, representing where a
1261 * drop at the given point should insert data.
1262 *
1263 * @param p the point to calculate a drop location for
1264 * @return the drop location, or <code>null</code>
1265 */
1266 DropLocation dropLocationForPoint(Point p) {
1267 DropLocation location = null;
1268 Rectangle rect = null;
1269
1270 int index = locationToIndex(p);
1271 if (index != -1) {
1272 rect = getCellBounds(index, index);
1273 }
1274
1275 switch(dropMode) {
1276 case USE_SELECTION:
1277 case ON:
1278 location = new DropLocation(p,
1279 (rect != null && rect.contains(p)) ? index : -1,
1280 false);
1281
1282 break;
1283 case INSERT:
1284 if (index == -1) {
1285 location = new DropLocation(p, getModel().getSize(), true);
1286 break;
1287 }
1288
1289 if (layoutOrientation == HORIZONTAL_WRAP) {
1290 boolean ltr = getComponentOrientation().isLeftToRight();
1291
1292 if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) {
1293 index++;
1294 // special case for below all cells
1295 } else if (index == getModel().getSize() - 1 && p.y >= rect.y + rect.height) {
1296 index++;
1297 }
1298 } else {
1299 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1300 index++;
1301 }
1302 }
1303
1304 location = new DropLocation(p, index, true);
1305
1306 break;
1307 case ON_OR_INSERT:
1308 if (index == -1) {
1309 location = new DropLocation(p, getModel().getSize(), true);
1310 break;
1311 }
1312
1313 boolean between = false;
1314
1315 if (layoutOrientation == HORIZONTAL_WRAP) {
1316 boolean ltr = getComponentOrientation().isLeftToRight();
1317
1318 Section section = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1319 if (section == TRAILING) {
1320 index++;
1321 between = true;
1322 // special case for below all cells
1323 } else if (index == getModel().getSize() - 1 && p.y >= rect.y + rect.height) {
1324 index++;
1325 between = true;
1326 } else if (section == LEADING) {
1327 between = true;
1328 }
1329 } else {
1330 Section section = SwingUtilities2.liesInVertical(rect, p, true);
1331 if (section == LEADING) {
1332 between = true;
1333 } else if (section == TRAILING) {
1334 index++;
1335 between = true;
1336 }
1337 }
1338
1339 location = new DropLocation(p, index, between);
1340
1341 break;
1342 default:
1343 assert false : "Unexpected drop mode";
1344 }
1345
1346 return location;
1347 }
1348
1349 /**
1350 * Called to set or clear the drop location during a DnD operation.
1351 * In some cases, the component may need to use it's internal selection
1352 * temporarily to indicate the drop location. To help facilitate this,
1353 * this method returns and accepts as a parameter a state object.
1354 * This state object can be used to store, and later restore, the selection
1355 * state. Whatever this method returns will be passed back to it in
1356 * future calls, as the state parameter. If it wants the DnD system to
1357 * continue storing the same state, it must pass it back every time.
1358 * Here's how this is used:
1359 * <p>
1360 * Let's say that on the first call to this method the component decides
1361 * to save some state (because it is about to use the selection to show
1362 * a drop index). It can return a state object to the caller encapsulating
1363 * any saved selection state. On a second call, let's say the drop location
1364 * is being changed to something else. The component doesn't need to
1365 * restore anything yet, so it simply passes back the same state object
1366 * to have the DnD system continue storing it. Finally, let's say this
1367 * method is messaged with <code>null</code>. This means DnD
1368 * is finished with this component for now, meaning it should restore
1369 * state. At this point, it can use the state parameter to restore
1370 * said state, and of course return <code>null</code> since there's
1371 * no longer anything to store.
1372 *
1373 * @param location the drop location (as calculated by
1374 * <code>dropLocationForPoint</code>) or <code>null</code>
1375 * if there's no longer a valid drop location
1376 * @param state the state object saved earlier for this component,
1377 * or <code>null</code>
1378 * @param forDrop whether or not the method is being called because an
1379 * actual drop occurred
1380 * @return any saved state for this component, or <code>null</code> if none
1381 */
1382 Object setDropLocation(TransferHandler.DropLocation location,
1383 Object state,
1384 boolean forDrop) {
1385
1386 Object retVal = null;
1387 DropLocation listLocation = (DropLocation)location;
1388
1389 if (dropMode == DropMode.USE_SELECTION) {
1390 if (listLocation == null) {
1391 if (!forDrop && state != null) {
1392 setSelectedIndices(((int[][])state)[0]);
1393
1394 int anchor = ((int[][])state)[1][0];
1395 int lead = ((int[][])state)[1][1];
1396
1397 SwingUtilities2.setLeadAnchorWithoutSelection(
1398 getSelectionModel(), lead, anchor);
1399 }
1400 } else {
1401 if (dropLocation == null) {
1402 int[] inds = getSelectedIndices();
1403 retVal = new int[][] {inds, {getAnchorSelectionIndex(),
1404 getLeadSelectionIndex()}};
1405 } else {
1406 retVal = state;
1407 }
1408
1409 int index = listLocation.getIndex();
1410 if (index == -1) {
1411 clearSelection();
1412 getSelectionModel().setAnchorSelectionIndex(-1);
1413 getSelectionModel().setLeadSelectionIndex(-1);
1414 } else {
1415 setSelectionInterval(index, index);
1416 }
1417 }
1418 }
1419
1420 DropLocation old = dropLocation;
1421 dropLocation = listLocation;
1422 firePropertyChange("dropLocation", old, dropLocation);
1423
1424 return retVal;
1425 }
1426
1427 /**
1428 * Returns the location that this component should visually indicate
1429 * as the drop location during a DnD operation over the component,
1430 * or {@code null} if no location is to currently be shown.
1431 * <p>
1432 * This method is not meant for querying the drop location
1433 * from a {@code TransferHandler}, as the drop location is only
1434 * set after the {@code TransferHandler}'s <code>canImport</code>
1435 * has returned and has allowed for the location to be shown.
1436 * <p>
1437 * When this property changes, a property change event with
1438 * name "dropLocation" is fired by the component.
1439 * <p>
1440 * By default, responsibility for listening for changes to this property
1441 * and indicating the drop location visually lies with the list's
1442 * {@code ListUI}, which may paint it directly and/or install a cell
1443 * renderer to do so. Developers wishing to implement custom drop location
1444 * painting and/or replace the default cell renderer, may need to honor
1445 * this property.
1446 *
1447 * @return the drop location
1448 * @see #setDropMode
1449 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1450 * @since 1.6
1451 */
1452 public final DropLocation getDropLocation() {
1453 return dropLocation;
1454 }
1455
1456 /**
1457 * Returns the next list element whose {@code toString} value
1458 * starts with the given prefix.
1459 *
1460 * @param prefix the string to test for a match
1461 * @param startIndex the index for starting the search
1462 * @param bias the search direction, either
1463 * Position.Bias.Forward or Position.Bias.Backward.
1464 * @return the index of the next list element that
1465 * starts with the prefix; otherwise {@code -1}
1466 * @exception IllegalArgumentException if prefix is {@code null}
1467 * or startIndex is out of bounds
1468 * @since 1.4
1469 */
1470 public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
1471 ListModel<E> model = getModel();
1472 int max = model.getSize();
1473 if (prefix == null) {
1474 throw new IllegalArgumentException();
1475 }
1476 if (startIndex < 0 || startIndex >= max) {
1477 throw new IllegalArgumentException();
1478 }
1479 prefix = prefix.toUpperCase();
1480
1481 // start search from the next element after the selected element
1482 int increment = (bias == Position.Bias.Forward) ? 1 : -1;
1483 int index = startIndex;
1484 do {
1485 E element = model.getElementAt(index);
1486
1487 if (element != null) {
1488 String string;
1489
1490 if (element instanceof String) {
1491 string = ((String)element).toUpperCase();
1492 }
1493 else {
1494 string = element.toString();
1495 if (string != null) {
1496 string = string.toUpperCase();
1497 }
1498 }
1499
1500 if (string != null && string.startsWith(prefix)) {
1501 return index;
1502 }
1503 }
1504 index = (index + increment + max) % max;
1505 } while (index != startIndex);
1506 return -1;
1507 }
1508
1509 /**
1510 * Returns the tooltip text to be used for the given event. This overrides
1511 * {@code JComponent}'s {@code getToolTipText} to first check the cell
1512 * renderer component for the cell over which the event occurred, returning
1513 * its tooltip text, if any. This implementation allows you to specify
1514 * tooltip text on the cell level, by using {@code setToolTipText} on your
1515 * cell renderer component.
1516 * <p>
1517 * <bold>Note:</bold> For <code>JList</code> to properly display the
1518 * tooltips of its renderers in this manner, <code>JList</code> must be a
1519 * registered component with the <code>ToolTipManager</code>. This registration
1520 * is done automatically in the constructor. However, if at a later point
1521 * <code>JList</code> is unregistered, by way of a call to
1522 * {@code setToolTipText(null)}, tips from the renderers will no longer display.
1523 *
1524 * @param event the {@code MouseEvent} to fetch the tooltip text for
1525 * @see JComponent#setToolTipText
1526 * @see JComponent#getToolTipText
1527 */
1528 public String getToolTipText(MouseEvent event) {
1529 if(event != null) {
1530 Point p = event.getPoint();
1531 int index = locationToIndex(p);
1532 ListCellRenderer<? super E> r = getCellRenderer();
1533 Rectangle cellBounds;
1534
1535 if (index != -1 && r != null && (cellBounds =
1536 getCellBounds(index, index)) != null &&
1537 cellBounds.contains(p.x, p.y)) {
1538 ListSelectionModel lsm = getSelectionModel();
1539 Component rComponent = r.getListCellRendererComponent(
1540 this, getModel().getElementAt(index), index,
1541 lsm.isSelectedIndex(index),
1542 (hasFocus() && (lsm.getLeadSelectionIndex() ==
1543 index)));
1544
1545 if(rComponent instanceof JComponent) {
1546 MouseEvent newEvent;
1547
1548 p.translate(-cellBounds.x, -cellBounds.y);
1549 newEvent = new MouseEvent(rComponent, event.getID(),
1550 event.getWhen(),
1551 event.getModifiers(),
1552 p.x, p.y,
1553 event.getXOnScreen(),
1554 event.getYOnScreen(),
1555 event.getClickCount(),
1556 event.isPopupTrigger(),
1557 MouseEvent.NOBUTTON);
1558
1559 String tip = ((JComponent)rComponent).getToolTipText(
1560 newEvent);
1561
1562 if (tip != null) {
1563 return tip;
1564 }
1565 }
1566 }
1567 }
1568 return super.getToolTipText();
1569 }
1570
1571 /**
1572 * --- ListUI Delegations ---
1573 */
1574
1575
1576 /**
1577 * Returns the cell index closest to the given location in the list's
1578 * coordinate system. To determine if the cell actually contains the
1579 * specified location, compare the point against the cell's bounds,
1580 * as provided by {@code getCellBounds}. This method returns {@code -1}
1581 * if the model is empty
1582 * <p>
1583 * This is a cover method that delegates to the method of the same name
1584 * in the list's {@code ListUI}. It returns {@code -1} if the list has
1585 * no {@code ListUI}.
1586 *
1587 * @param location the coordinates of the point
1588 * @return the cell index closest to the given location, or {@code -1}
1589 */
1590 public int locationToIndex(Point location) {
1591 ListUI ui = getUI();
1592 return (ui != null) ? ui.locationToIndex(this, location) : -1;
1593 }
1594
1595
1596 /**
1597 * Returns the origin of the specified item in the list's coordinate
1598 * system. This method returns {@code null} if the index isn't valid.
1599 * <p>
1600 * This is a cover method that delegates to the method of the same name
1601 * in the list's {@code ListUI}. It returns {@code null} if the list has
1602 * no {@code ListUI}.
1603 *
1604 * @param index the cell index
1605 * @return the origin of the cell, or {@code null}
1606 */
1607 public Point indexToLocation(int index) {
1608 ListUI ui = getUI();
1609 return (ui != null) ? ui.indexToLocation(this, index) : null;
1610 }
1611
1612
1613 /**
1614 * Returns the bounding rectangle, in the list's coordinate system,
1615 * for the range of cells specified by the two indices.
1616 * These indices can be supplied in any order.
1617 * <p>
1618 * If the smaller index is outside the list's range of cells, this method
1619 * returns {@code null}. If the smaller index is valid, but the larger
1620 * index is outside the list's range, the bounds of just the first index
1621 * is returned. Otherwise, the bounds of the valid range is returned.
1622 * <p>
1623 * This is a cover method that delegates to the method of the same name
1624 * in the list's {@code ListUI}. It returns {@code null} if the list has
1625 * no {@code ListUI}.
1626 *
1627 * @param index0 the first index in the range
1628 * @param index1 the second index in the range
1629 * @return the bounding rectangle for the range of cells, or {@code null}
1630 */
1631 public Rectangle getCellBounds(int index0, int index1) {
1632 ListUI ui = getUI();
1633 return (ui != null) ? ui.getCellBounds(this, index0, index1) : null;
1634 }
1635
1636
1637 /**
1638 * --- ListModel Support ---
1639 */
1640
1641
1642 /**
1643 * Returns the data model that holds the list of items displayed
1644 * by the <code>JList</code> component.
1645 *
1646 * @return the <code>ListModel</code> that provides the displayed
1647 * list of items
1648 * @see #setModel
1649 */
1650 public ListModel<E> getModel() {
1651 return dataModel;
1652 }
1653
1654 /**
1655 * Sets the model that represents the contents or "value" of the
1656 * list, notifies property change listeners, and then clears the
1657 * list's selection.
1658 * <p>
1659 * This is a JavaBeans bound property.
1660 *
1661 * @param model the <code>ListModel</code> that provides the
1662 * list of items for display
1663 * @exception IllegalArgumentException if <code>model</code> is
1664 * <code>null</code>
1665 * @see #getModel
1666 * @see #clearSelection
1667 * @beaninfo
1668 * bound: true
1669 * attribute: visualUpdate true
1670 * description: The object that contains the data to be drawn by this JList.
1671 */
1672 public void setModel(ListModel<E> model) {
1673 if (model == null) {
1674 throw new IllegalArgumentException("model must be non null");
1675 }
1676 ListModel<E> oldValue = dataModel;
1677 dataModel = model;
1678 firePropertyChange("model", oldValue, dataModel);
1679 clearSelection();
1680 }
1681
1682
1683 /**
1684 * Constructs a read-only <code>ListModel</code> from an array of items,
1685 * and calls {@code setModel} with this model.
1686 * <p>
1687 * Attempts to pass a {@code null} value to this method results in
1688 * undefined behavior and, most likely, exceptions. The created model
1689 * references the given array directly. Attempts to modify the array
1690 * after invoking this method results in undefined behavior.
1691 *
1692 * @param listData an array of {@code E} containing the items to
1693 * display in the list
1694 * @see #setModel
1695 */
1696 public void setListData(final E[] listData) {
1697 setModel (
1698 new AbstractListModel<E>() {
1699 public int getSize() { return listData.length; }
1700 public E getElementAt(int i) { return listData[i]; }
1701 }
1702 );
1703 }
1704
1705
1706 /**
1707 * Constructs a read-only <code>ListModel</code> from a <code>Vector</code>
1708 * and calls {@code setModel} with this model.
1709 * <p>
1710 * Attempts to pass a {@code null} value to this method results in
1711 * undefined behavior and, most likely, exceptions. The created model
1712 * references the given {@code Vector} directly. Attempts to modify the
1713 * {@code Vector} after invoking this method results in undefined behavior.
1714 *
1715 * @param listData a <code>Vector</code> containing the items to
1716 * display in the list
1717 * @see #setModel
1718 */
1719 public void setListData(final Vector<? extends E> listData) {
1720 setModel (
1721 new AbstractListModel<E>() {
1722 public int getSize() { return listData.size(); }
1723 public E getElementAt(int i) { return listData.elementAt(i); }
1724 }
1725 );
1726 }
1727
1728
1729 /**
1730 * --- ListSelectionModel delegations and extensions ---
1731 */
1732
1733
1734 /**
1735 * Returns an instance of {@code DefaultListSelectionModel}; called
1736 * during construction to initialize the list's selection model
1737 * property.
1738 *
1739 * @return a {@code DefaultListSelecitonModel}, used to initialize
1740 * the list's selection model property during construction
1741 * @see #setSelectionModel
1742 * @see DefaultListSelectionModel
1743 */
1744 protected ListSelectionModel createSelectionModel() {
1745 return new DefaultListSelectionModel();
1746 }
1747
1748
1749 /**
1750 * Returns the current selection model. The selection model maintains the
1751 * selection state of the list. See the class level documentation for more
1752 * details.
1753 *
1754 * @return the <code>ListSelectionModel</code> that maintains the
1755 * list's selections
1756 *
1757 * @see #setSelectionModel
1758 * @see ListSelectionModel
1759 */
1760 public ListSelectionModel getSelectionModel() {
1761 return selectionModel;
1762 }
1763
1764
1765 /**
1766 * Notifies {@code ListSelectionListener}s added directly to the list
1767 * of selection changes made to the selection model. {@code JList}
1768 * listens for changes made to the selection in the selection model,
1769 * and forwards notification to listeners added to the list directly,
1770 * by calling this method.
1771 * <p>
1772 * This method constructs a {@code ListSelectionEvent} with this list
1773 * as the source, and the specified arguments, and sends it to the
1774 * registered {@code ListSelectionListeners}.
1775 *
1776 * @param firstIndex the first index in the range, {@code <= lastIndex}
1777 * @param lastIndex the last index in the range, {@code >= firstIndex}
1778 * @param isAdjusting whether or not this is one in a series of
1779 * multiple events, where changes are still being made
1780 *
1781 * @see #addListSelectionListener
1782 * @see #removeListSelectionListener
1783 * @see javax.swing.event.ListSelectionEvent
1784 * @see EventListenerList
1785 */
1786 protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
1787 boolean isAdjusting)
1788 {
1789 Object[] listeners = listenerList.getListenerList();
1790 ListSelectionEvent e = null;
1791
1792 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1793 if (listeners[i] == ListSelectionListener.class) {
1794 if (e == null) {
1795 e = new ListSelectionEvent(this, firstIndex, lastIndex,
1796 isAdjusting);
1797 }
1798 ((ListSelectionListener)listeners[i+1]).valueChanged(e);
1799 }
1800 }
1801 }
1802
1803
1804 /* A ListSelectionListener that forwards ListSelectionEvents from
1805 * the selectionModel to the JList ListSelectionListeners. The
1806 * forwarded events only differ from the originals in that their
1807 * source is the JList instead of the selectionModel itself.
1808 */
1809 private class ListSelectionHandler implements ListSelectionListener, Serializable
1810 {
1811 public void valueChanged(ListSelectionEvent e) {
1812 fireSelectionValueChanged(e.getFirstIndex(),
1813 e.getLastIndex(),
1814 e.getValueIsAdjusting());
1815 }
1816 }
1817
1818
1819 /**
1820 * Adds a listener to the list, to be notified each time a change to the
1821 * selection occurs; the preferred way of listening for selection state
1822 * changes. {@code JList} takes care of listening for selection state
1823 * changes in the selection model, and notifies the given listener of
1824 * each change. {@code ListSelectionEvent}s sent to the listener have a
1825 * {@code source} property set to this list.
1826 *
1827 * @param listener the {@code ListSelectionListener} to add
1828 * @see #getSelectionModel
1829 * @see #getListSelectionListeners
1830 */
1831 public void addListSelectionListener(ListSelectionListener listener)
1832 {
1833 if (selectionListener == null) {
1834 selectionListener = new ListSelectionHandler();
1835 getSelectionModel().addListSelectionListener(selectionListener);
1836 }
1837
1838 listenerList.add(ListSelectionListener.class, listener);
1839 }
1840
1841
1842 /**
1843 * Removes a selection listener from the list.
1844 *
1845 * @param listener the {@code ListSelectionListener} to remove
1846 * @see #addListSelectionListener
1847 * @see #getSelectionModel
1848 */
1849 public void removeListSelectionListener(ListSelectionListener listener) {
1850 listenerList.remove(ListSelectionListener.class, listener);
1851 }
1852
1853
1854 /**
1855 * Returns an array of all the {@code ListSelectionListener}s added
1856 * to this {@code JList} by way of {@code addListSelectionListener}.
1857 *
1858 * @return all of the {@code ListSelectionListener}s on this list, or
1859 * an empty array if no listeners have been added
1860 * @see #addListSelectionListener
1861 * @since 1.4
1862 */
1863 public ListSelectionListener[] getListSelectionListeners() {
1864 return listenerList.getListeners(ListSelectionListener.class);
1865 }
1866
1867
1868 /**
1869 * Sets the <code>selectionModel</code> for the list to a
1870 * non-<code>null</code> <code>ListSelectionModel</code>
1871 * implementation. The selection model handles the task of making single
1872 * selections, selections of contiguous ranges, and non-contiguous
1873 * selections.
1874 * <p>
1875 * This is a JavaBeans bound property.
1876 *
1877 * @param selectionModel the <code>ListSelectionModel</code> that
1878 * implements the selections
1879 * @exception IllegalArgumentException if <code>selectionModel</code>
1880 * is <code>null</code>
1881 * @see #getSelectionModel
1882 * @beaninfo
1883 * bound: true
1884 * description: The selection model, recording which cells are selected.
1885 */
1886 public void setSelectionModel(ListSelectionModel selectionModel) {
1887 if (selectionModel == null) {
1888 throw new IllegalArgumentException("selectionModel must be non null");
1889 }
1890
1891 /* Remove the forwarding ListSelectionListener from the old
1892 * selectionModel, and add it to the new one, if necessary.
1893 */
1894 if (selectionListener != null) {
1895 this.selectionModel.removeListSelectionListener(selectionListener);
1896 selectionModel.addListSelectionListener(selectionListener);
1897 }
1898
1899 ListSelectionModel oldValue = this.selectionModel;
1900 this.selectionModel = selectionModel;
1901 firePropertyChange("selectionModel", oldValue, selectionModel);
1902 }
1903
1904
1905 /**
1906 * Sets the selection mode for the list. This is a cover method that sets
1907 * the selection mode directly on the selection model.
1908 * <p>
1909 * The following list describes the accepted selection modes:
1910 * <ul>
1911 * <li>{@code ListSelectionModel.SINGLE_SELECTION} -
1912 * Only one list index can be selected at a time. In this mode,
1913 * {@code setSelectionInterval} and {@code addSelectionInterval} are
1914 * equivalent, both replacing the current selection with the index
1915 * represented by the second argument (the "lead").
1916 * <li>{@code ListSelectionModel.SINGLE_INTERVAL_SELECTION} -
1917 * Only one contiguous interval can be selected at a time.
1918 * In this mode, {@code addSelectionInterval} behaves like
1919 * {@code setSelectionInterval} (replacing the current selection},
1920 * unless the given interval is immediately adjacent to or overlaps
1921 * the existing selection, and can be used to grow the selection.
1922 * <li>{@code ListSelectionModel.MULTIPLE_INTERVAL_SELECTION} -
1923 * In this mode, there's no restriction on what can be selected.
1924 * This mode is the default.
1925 * </ul>
1926 *
1927 * @param selectionMode the selection mode
1928 * @see #getSelectionMode
1929 * @throws IllegalArgumentException if the selection mode isn't
1930 * one of those allowed
1931 * @beaninfo
1932 * description: The selection mode.
1933 * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
1934 * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
1935 * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
1936 */
1937 public void setSelectionMode(int selectionMode) {
1938 getSelectionModel().setSelectionMode(selectionMode);
1939 }
1940
1941 /**
1942 * Returns the current selection mode for the list. This is a cover
1943 * method that delegates to the method of the same name on the
1944 * list's selection model.
1945 *
1946 * @return the current selection mode
1947 * @see #setSelectionMode
1948 */
1949 public int getSelectionMode() {
1950 return getSelectionModel().getSelectionMode();
1951 }
1952
1953
1954 /**
1955 * Returns the anchor selection index. This is a cover method that
1956 * delegates to the method of the same name on the list's selection model.
1957 *
1958 * @return the anchor selection index
1959 * @see ListSelectionModel#getAnchorSelectionIndex
1960 */
1961 public int getAnchorSelectionIndex() {
1962 return getSelectionModel().getAnchorSelectionIndex();
1963 }
1964
1965
1966 /**
1967 * Returns the lead selection index. This is a cover method that
1968 * delegates to the method of the same name on the list's selection model.
1969 *
1970 * @return the lead selection index
1971 * @see ListSelectionModel#getLeadSelectionIndex
1972 * @beaninfo
1973 * description: The lead selection index.
1974 */
1975 public int getLeadSelectionIndex() {
1976 return getSelectionModel().getLeadSelectionIndex();
1977 }
1978
1979
1980 /**
1981 * Returns the smallest selected cell index, or {@code -1} if the selection
1982 * is empty. This is a cover method that delegates to the method of the same
1983 * name on the list's selection model.
1984 *
1985 * @return the smallest selected cell index, or {@code -1}
1986 * @see ListSelectionModel#getMinSelectionIndex
1987 */
1988 public int getMinSelectionIndex() {
1989 return getSelectionModel().getMinSelectionIndex();
1990 }
1991
1992
1993 /**
1994 * Returns the largest selected cell index, or {@code -1} if the selection
1995 * is empty. This is a cover method that delegates to the method of the same
1996 * name on the list's selection model.
1997 *
1998 * @return the largest selected cell index
1999 * @see ListSelectionModel#getMaxSelectionIndex
2000 */
2001 public int getMaxSelectionIndex() {
2002 return getSelectionModel().getMaxSelectionIndex();
2003 }
2004
2005
2006 /**
2007 * Returns {@code true} if the specified index is selected,
2008 * else {@code false}. This is a cover method that delegates to the method
2009 * of the same name on the list's selection model.
2010 *
2011 * @param index index to be queried for selection state
2012 * @return {@code true} if the specified index is selected,
2013 * else {@code false}
2014 * @see ListSelectionModel#isSelectedIndex
2015 * @see #setSelectedIndex
2016 */
2017 public boolean isSelectedIndex(int index) {
2018 return getSelectionModel().isSelectedIndex(index);
2019 }
2020
2021
2022 /**
2023 * Returns {@code true} if nothing is selected, else {@code false}.
2024 * This is a cover method that delegates to the method of the same
2025 * name on the list's selection model.
2026 *
2027 * @return {@code true} if nothing is selected, else {@code false}
2028 * @see ListSelectionModel#isSelectionEmpty
2029 * @see #clearSelection
2030 */
2031 public boolean isSelectionEmpty() {
2032 return getSelectionModel().isSelectionEmpty();
2033 }
2034
2035
2036 /**
2037 * Clears the selection; after calling this method, {@code isSelectionEmpty}
2038 * will return {@code true}. This is a cover method that delegates to the
2039 * method of the same name on the list's selection model.
2040 *
2041 * @see ListSelectionModel#clearSelection
2042 * @see #isSelectionEmpty
2043 */
2044 public void clearSelection() {
2045 getSelectionModel().clearSelection();
2046 }
2047
2048
2049 /**
2050 * Selects the specified interval. Both {@code anchor} and {@code lead}
2051 * indices are included. {@code anchor} doesn't have to be less than or
2052 * equal to {@code lead}. This is a cover method that delegates to the
2053 * method of the same name on the list's selection model.
2054 * <p>
2055 * Refer to the documentation of the selection model class being used
2056 * for details on how values less than {@code 0} are handled.
2057 *
2058 * @param anchor the first index to select
2059 * @param lead the last index to select
2060 * @see ListSelectionModel#setSelectionInterval
2061 * @see DefaultListSelectionModel#setSelectionInterval
2062 * @see #createSelectionModel
2063 * @see #addSelectionInterval
2064 * @see #removeSelectionInterval
2065 */
2066 public void setSelectionInterval(int anchor, int lead) {
2067 getSelectionModel().setSelectionInterval(anchor, lead);
2068 }
2069
2070
2071 /**
2072 * Sets the selection to be the union of the specified interval with current
2073 * selection. Both the {@code anchor} and {@code lead} indices are
2074 * included. {@code anchor} doesn't have to be less than or
2075 * equal to {@code lead}. This is a cover method that delegates to the
2076 * method of the same name on the list's selection model.
2077 * <p>
2078 * Refer to the documentation of the selection model class being used
2079 * for details on how values less than {@code 0} are handled.
2080 *
2081 * @param anchor the first index to add to the selection
2082 * @param lead the last index to add to the selection
2083 * @see ListSelectionModel#addSelectionInterval
2084 * @see DefaultListSelectionModel#addSelectionInterval
2085 * @see #createSelectionModel
2086 * @see #setSelectionInterval
2087 * @see #removeSelectionInterval
2088 */
2089 public void addSelectionInterval(int anchor, int lead) {
2090 getSelectionModel().addSelectionInterval(anchor, lead);
2091 }
2092
2093
2094 /**
2095 * Sets the selection to be the set difference of the specified interval
2096 * and the current selection. Both the {@code index0} and {@code index1}
2097 * indices are removed. {@code index0} doesn't have to be less than or
2098 * equal to {@code index1}. This is a cover method that delegates to the
2099 * method of the same name on the list's selection model.
2100 * <p>
2101 * Refer to the documentation of the selection model class being used
2102 * for details on how values less than {@code 0} are handled.
2103 *
2104 * @param index0 the first index to remove from the selection
2105 * @param index1 the last index to remove from the selection
2106 * @see ListSelectionModel#removeSelectionInterval
2107 * @see DefaultListSelectionModel#removeSelectionInterval
2108 * @see #createSelectionModel
2109 * @see #setSelectionInterval
2110 * @see #addSelectionInterval
2111 */
2112 public void removeSelectionInterval(int index0, int index1) {
2113 getSelectionModel().removeSelectionInterval(index0, index1);
2114 }
2115
2116
2117 /**
2118 * Sets the selection model's {@code valueIsAdjusting} property. When
2119 * {@code true}, upcoming changes to selection should be considered part
2120 * of a single change. This property is used internally and developers
2121 * typically need not call this method. For example, when the model is being
2122 * updated in response to a user drag, the value of the property is set
2123 * to {@code true} when the drag is initiated and set to {@code false}
2124 * when the drag is finished. This allows listeners to update only
2125 * when a change has been finalized, rather than handling all of the
2126 * intermediate values.
2127 * <p>
2128 * You may want to use this directly if making a series of changes
2129 * that should be considered part of a single change.
2130 * <p>
2131 * This is a cover method that delegates to the method of the same name on
2132 * the list's selection model. See the documentation for
2133 * {@link javax.swing.ListSelectionModel#setValueIsAdjusting} for
2134 * more details.
2135 *
2136 * @param b the new value for the property
2137 * @see ListSelectionModel#setValueIsAdjusting
2138 * @see javax.swing.event.ListSelectionEvent#getValueIsAdjusting
2139 * @see #getValueIsAdjusting
2140 */
2141 public void setValueIsAdjusting(boolean b) {
2142 getSelectionModel().setValueIsAdjusting(b);
2143 }
2144
2145
2146 /**
2147 * Returns the value of the selection model's {@code isAdjusting} property.
2148 * <p>
2149 * This is a cover method that delegates to the method of the same name on
2150 * the list's selection model.
2151 *
2152 * @return the value of the selection model's {@code isAdjusting} property.
2153 *
2154 * @see #setValueIsAdjusting
2155 * @see ListSelectionModel#getValueIsAdjusting
2156 */
2157 public boolean getValueIsAdjusting() {
2158 return getSelectionModel().getValueIsAdjusting();
2159 }
2160
2161
2162 /**
2163 * Returns an array of all of the selected indices, in increasing
2164 * order.
2165 *
2166 * @return all of the selected indices, in increasing order,
2167 * or an empty array if nothing is selected
2168 * @see #removeSelectionInterval
2169 * @see #addListSelectionListener
2170 */
2171 @Transient
2172 public int[] getSelectedIndices() {
2173 ListSelectionModel sm = getSelectionModel();
2174 int iMin = sm.getMinSelectionIndex();
2175 int iMax = sm.getMaxSelectionIndex();
2176
2177 if ((iMin < 0) || (iMax < 0)) {
2178 return new int[0];
2179 }
2180
2181 int[] rvTmp = new int[1+ (iMax - iMin)];
2182 int n = 0;
2183 for(int i = iMin; i <= iMax; i++) {
2184 if (sm.isSelectedIndex(i)) {
2185 rvTmp[n++] = i;
2186 }
2187 }
2188 int[] rv = new int[n];
2189 System.arraycopy(rvTmp, 0, rv, 0, n);
2190 return rv;
2191 }
2192
2193
2194 /**
2195 * Selects a single cell. Does nothing if the given index is greater
2196 * than or equal to the model size. This is a convenience method that uses
2197 * {@code setSelectionInterval} on the selection model. Refer to the
2198 * documentation for the selection model class being used for details on
2199 * how values less than {@code 0} are handled.
2200 *
2201 * @param index the index of the cell to select
2202 * @see ListSelectionModel#setSelectionInterval
2203 * @see #isSelectedIndex
2204 * @see #addListSelectionListener
2205 * @beaninfo
2206 * description: The index of the selected cell.
2207 */
2208 public void setSelectedIndex(int index) {
2209 if (index >= getModel().getSize()) {
2210 return;
2211 }
2212 getSelectionModel().setSelectionInterval(index, index);
2213 }
2214
2215
2216 /**
2217 * Changes the selection to be the set of indices specified by the given
2218 * array. Indices greater than or equal to the model size are ignored.
2219 * This is a convenience method that clears the selection and then uses
2220 * {@code addSelectionInterval} on the selection model to add the indices.
2221 * Refer to the documentation of the selection model class being used for
2222 * details on how values less than {@code 0} are handled.
2223 *
2224 * @param indices an array of the indices of the cells to select,
2225 * {@code non-null}
2226 * @see ListSelectionModel#addSelectionInterval
2227 * @see #isSelectedIndex
2228 * @see #addListSelectionListener
2229 * @throws NullPointerException if the given array is {@code null}
2230 */
2231 public void setSelectedIndices(int[] indices) {
2232 ListSelectionModel sm = getSelectionModel();
2233 sm.clearSelection();
2234 int size = getModel().getSize();
2235 for (int i : indices) {
2236 if (i < size) {
2237 sm.addSelectionInterval(i, i);
2238 }
2239 }
2240 }
2241
2242
2243 /**
2244 * Returns an array of all the selected values, in increasing order based
2245 * on their indices in the list.
2246 *
2247 * @return the selected values, or an empty array if nothing is selected
2248 * @see #isSelectedIndex
2249 * @see #getModel
2250 * @see #addListSelectionListener
2251 *
2252 * @deprecated As of JDK 1.7, replaced by {@link #getSelectedValuesList()}
2253 */
2254 @Deprecated
2255 public Object[] getSelectedValues() {
2256 ListSelectionModel sm = getSelectionModel();
2257 ListModel<E> dm = getModel();
2258
2259 int iMin = sm.getMinSelectionIndex();
2260 int iMax = sm.getMaxSelectionIndex();
2261
2262 if ((iMin < 0) || (iMax < 0)) {
2263 return new Object[0];
2264 }
2265
2266 Object[] rvTmp = new Object[1+ (iMax - iMin)];
2267 int n = 0;
2268 for(int i = iMin; i <= iMax; i++) {
2269 if (sm.isSelectedIndex(i)) {
2270 rvTmp[n++] = dm.getElementAt(i);
2271 }
2272 }
2273 Object[] rv = new Object[n];
2274 System.arraycopy(rvTmp, 0, rv, 0, n);
2275 return rv;
2276 }
2277
2278 /**
2279 * Returns a list of all the selected items, in increasing order based
2280 * on their indices in the list.
2281 *
2282 * @return the selected items, or an empty list if nothing is selected
2283 * @see #isSelectedIndex
2284 * @see #getModel
2285 * @see #addListSelectionListener
2286 *
2287 * @since 1.7
2288 */
2289 public List<E> getSelectedValuesList() {
2290 ListSelectionModel sm = getSelectionModel();
2291 ListModel<E> dm = getModel();
2292
2293 int iMin = sm.getMinSelectionIndex();
2294 int iMax = sm.getMaxSelectionIndex();
2295
2296 if ((iMin < 0) || (iMax < 0)) {
2297 return Collections.emptyList();
2298 }
2299
2300 List<E> selectedItems = new ArrayList<E>();
2301 for(int i = iMin; i <= iMax; i++) {
2302 if (sm.isSelectedIndex(i)) {
2303 selectedItems.add(dm.getElementAt(i));
2304 }
2305 }
2306 return selectedItems;
2307 }
2308
2309
2310 /**
2311 * Returns the smallest selected cell index; <i>the selection</i> when only
2312 * a single item is selected in the list. When multiple items are selected,
2313 * it is simply the smallest selected index. Returns {@code -1} if there is
2314 * no selection.
2315 * <p>
2316 * This method is a cover that delegates to {@code getMinSelectionIndex}.
2317 *
2318 * @return the smallest selected cell index
2319 * @see #getMinSelectionIndex
2320 * @see #addListSelectionListener
2321 */
2322 public int getSelectedIndex() {
2323 return getMinSelectionIndex();
2324 }
2325
2326
2327 /**
2328 * Returns the value for the smallest selected cell index;
2329 * <i>the selected value</i> when only a single item is selected in the
2330 * list. When multiple items are selected, it is simply the value for the
2331 * smallest selected index. Returns {@code null} if there is no selection.
2332 * <p>
2333 * This is a convenience method that simply returns the model value for
2334 * {@code getMinSelectionIndex}.
2335 *
2336 * @return the first selected value
2337 * @see #getMinSelectionIndex
2338 * @see #getModel
2339 * @see #addListSelectionListener
2340 */
2341 public E getSelectedValue() {
2342 int i = getMinSelectionIndex();
2343 return (i == -1) ? null : getModel().getElementAt(i);
2344 }
2345
2346
2347 /**
2348 * Selects the specified object from the list.
2349 *
2350 * @param anObject the object to select
2351 * @param shouldScroll {@code true} if the list should scroll to display
2352 * the selected object, if one exists; otherwise {@code false}
2353 */
2354 public void setSelectedValue(Object anObject,boolean shouldScroll) {
2355 if(anObject == null)
2356 setSelectedIndex(-1);
2357 else if(!anObject.equals(getSelectedValue())) {
2358 int i,c;
2359 ListModel<E> dm = getModel();
2360 for(i=0,c=dm.getSize();i<c;i++)
2361 if(anObject.equals(dm.getElementAt(i))){
2362 setSelectedIndex(i);
2363 if(shouldScroll)
2364 ensureIndexIsVisible(i);
2365 repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
2366 return;
2367 }
2368 setSelectedIndex(-1);
2369 }
2370 repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
2371 }
2372
2373
2374
2375 /**
2376 * --- The Scrollable Implementation ---
2377 */
2378
2379 private void checkScrollableParameters(Rectangle visibleRect, int orientation) {
2380 if (visibleRect == null) {
2381 throw new IllegalArgumentException("visibleRect must be non-null");
2382 }
2383 switch (orientation) {
2384 case SwingConstants.VERTICAL:
2385 case SwingConstants.HORIZONTAL:
2386 break;
2387 default:
2388 throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
2389 }
2390 }
2391
2392
2393 /**
2394 * Computes the size of viewport needed to display {@code visibleRowCount}
2395 * rows. The value returned by this method depends on the layout
2396 * orientation:
2397 * <p>
2398 * <b>{@code VERTICAL}:</b>
2399 * <br>
2400 * This is trivial if both {@code fixedCellWidth} and {@code fixedCellHeight}
2401 * have been set (either explicitly or by specifying a prototype cell value).
2402 * The width is simply the {@code fixedCellWidth} plus the list's horizontal
2403 * insets. The height is the {@code fixedCellHeight} multiplied by the
2404 * {@code visibleRowCount}, plus the list's vertical insets.
2405 * <p>
2406 * If either {@code fixedCellWidth} or {@code fixedCellHeight} haven't been
2407 * specified, heuristics are used. If the model is empty, the width is
2408 * the {@code fixedCellWidth}, if greater than {@code 0}, or a hard-coded
2409 * value of {@code 256}. The height is the {@code fixedCellHeight} multiplied
2410 * by {@code visibleRowCount}, if {@code fixedCellHeight} is greater than
2411 * {@code 0}, otherwise it is a hard-coded value of {@code 16} multiplied by
2412 * {@code visibleRowCount}.
2413 * <p>
2414 * If the model isn't empty, the width is the preferred size's width,
2415 * typically the width of the widest list element. The height is the
2416 * {@code fixedCellHeight} multiplied by the {@code visibleRowCount},
2417 * plus the list's vertical insets.
2418 * <p>
2419 * <b>{@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:</b>
2420 * <br>
2421 * This method simply returns the value from {@code getPreferredSize}.
2422 * The list's {@code ListUI} is expected to override {@code getPreferredSize}
2423 * to return an appropriate value.
2424 *
2425 * @return a dimension containing the size of the viewport needed
2426 * to display {@code visibleRowCount} rows
2427 * @see #getPreferredScrollableViewportSize
2428 * @see #setPrototypeCellValue
2429 */
2430 public Dimension getPreferredScrollableViewportSize()
2431 {
2432 if (getLayoutOrientation() != VERTICAL) {
2433 return getPreferredSize();
2434 }
2435 Insets insets = getInsets();
2436 int dx = insets.left + insets.right;
2437 int dy = insets.top + insets.bottom;
2438
2439 int visibleRowCount = getVisibleRowCount();
2440 int fixedCellWidth = getFixedCellWidth();
2441 int fixedCellHeight = getFixedCellHeight();
2442
2443 if ((fixedCellWidth > 0) && (fixedCellHeight > 0)) {
2444 int width = fixedCellWidth + dx;
2445 int height = (visibleRowCount * fixedCellHeight) + dy;
2446 return new Dimension(width, height);
2447 }
2448 else if (getModel().getSize() > 0) {
2449 int width = getPreferredSize().width;
2450 int height;
2451 Rectangle r = getCellBounds(0, 0);
2452 if (r != null) {
2453 height = (visibleRowCount * r.height) + dy;
2454 }
2455 else {
2456 // Will only happen if UI null, shouldn't matter what we return
2457 height = 1;
2458 }
2459 return new Dimension(width, height);
2460 }
2461 else {
2462 fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth : 256;
2463 fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight : 16;
2464 return new Dimension(fixedCellWidth, fixedCellHeight * visibleRowCount);
2465 }
2466 }
2467
2468
2469 /**
2470 * Returns the distance to scroll to expose the next or previous
2471 * row (for vertical scrolling) or column (for horizontal scrolling).
2472 * <p>
2473 * For horizontal scrolling, if the layout orientation is {@code VERTICAL},
2474 * then the list's font size is returned (or {@code 1} if the font is
2475 * {@code null}).
2476 *
2477 * @param visibleRect the view area visible within the viewport
2478 * @param orientation {@code SwingConstants.HORIZONTAL} or
2479 * {@code SwingConstants.VERTICAL}
2480 * @param direction less or equal to zero to scroll up/back,
2481 * greater than zero for down/forward
2482 * @return the "unit" increment for scrolling in the specified direction;
2483 * always positive
2484 * @see #getScrollableBlockIncrement
2485 * @see Scrollable#getScrollableUnitIncrement
2486 * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
2487 * {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
2488 * {@code SwingConstants.HORIZONTAL}
2489 */
2490 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
2491 {
2492 checkScrollableParameters(visibleRect, orientation);
2493
2494 if (orientation == SwingConstants.VERTICAL) {
2495 int row = locationToIndex(visibleRect.getLocation());
2496
2497 if (row == -1) {
2498 return 0;
2499 }
2500 else {
2501 /* Scroll Down */
2502 if (direction > 0) {
2503 Rectangle r = getCellBounds(row, row);
2504 return (r == null) ? 0 : r.height - (visibleRect.y - r.y);
2505 }
2506 /* Scroll Up */
2507 else {
2508 Rectangle r = getCellBounds(row, row);
2509
2510 /* The first row is completely visible and it's row 0.
2511 * We're done.
2512 */
2513 if ((r.y == visibleRect.y) && (row == 0)) {
2514 return 0;
2515 }
2516 /* The first row is completely visible, return the
2517 * height of the previous row or 0 if the first row
2518 * is the top row of the list.
2519 */
2520 else if (r.y == visibleRect.y) {
2521 Point loc = r.getLocation();
2522 loc.y--;
2523 int prevIndex = locationToIndex(loc);
2524 Rectangle prevR = getCellBounds(prevIndex, prevIndex);
2525
2526 if (prevR == null || prevR.y >= r.y) {
2527 return 0;
2528 }
2529 return prevR.height;
2530 }
2531 /* The first row is partially visible, return the
2532 * height of hidden part.
2533 */
2534 else {
2535 return visibleRect.y - r.y;
2536 }
2537 }
2538 }
2539 } else if (orientation == SwingConstants.HORIZONTAL &&
2540 getLayoutOrientation() != JList.VERTICAL) {
2541 boolean leftToRight = getComponentOrientation().isLeftToRight();
2542 int index;
2543 Point leadingPoint;
2544
2545 if (leftToRight) {
2546 leadingPoint = visibleRect.getLocation();
2547 }
2548 else {
2549 leadingPoint = new Point(visibleRect.x + visibleRect.width -1,
2550 visibleRect.y);
2551 }
2552 index = locationToIndex(leadingPoint);
2553
2554 if (index != -1) {
2555 Rectangle cellBounds = getCellBounds(index, index);
2556 if (cellBounds != null && cellBounds.contains(leadingPoint)) {
2557 int leadingVisibleEdge;
2558 int leadingCellEdge;
2559
2560 if (leftToRight) {
2561 leadingVisibleEdge = visibleRect.x;
2562 leadingCellEdge = cellBounds.x;
2563 }
2564 else {
2565 leadingVisibleEdge = visibleRect.x + visibleRect.width;
2566 leadingCellEdge = cellBounds.x + cellBounds.width;
2567 }
2568
2569 if (leadingCellEdge != leadingVisibleEdge) {
2570 if (direction < 0) {
2571 // Show remainder of leading cell
2572 return Math.abs(leadingVisibleEdge - leadingCellEdge);
2573
2574 }
2575 else if (leftToRight) {
2576 // Hide rest of leading cell
2577 return leadingCellEdge + cellBounds.width - leadingVisibleEdge;
2578 }
2579 else {
2580 // Hide rest of leading cell
2581 return leadingVisibleEdge - cellBounds.x;
2582 }
2583 }
2584 // ASSUME: All cells are the same width
2585 return cellBounds.width;
2586 }
2587 }
2588 }
2589 Font f = getFont();
2590 return (f != null) ? f.getSize() : 1;
2591 }
2592
2593
2594 /**
2595 * Returns the distance to scroll to expose the next or previous block.
2596 * <p>
2597 * For vertical scrolling, the following rules are used:
2598 * <ul>
2599 * <li>if scrolling down, returns the distance to scroll so that the last
2600 * visible element becomes the first completely visible element
2601 * <li>if scrolling up, returns the distance to scroll so that the first
2602 * visible element becomes the last completely visible element
2603 * <li>returns {@code visibleRect.height} if the list is empty
2604 * </ul>
2605 * <p>
2606 * For horizontal scrolling, when the layout orientation is either
2607 * {@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:
2608 * <ul>
2609 * <li>if scrolling right, returns the distance to scroll so that the
2610 * last visible element becomes
2611 * the first completely visible element
2612 * <li>if scrolling left, returns the distance to scroll so that the first
2613 * visible element becomes the last completely visible element
2614 * <li>returns {@code visibleRect.width} if the list is empty
2615 * </ul>
2616 * <p>
2617 * For horizontal scrolling and {@code VERTICAL} orientation,
2618 * returns {@code visibleRect.width}.
2619 * <p>
2620 * Note that the value of {@code visibleRect} must be the equal to
2621 * {@code this.getVisibleRect()}.
2622 *
2623 * @param visibleRect the view area visible within the viewport
2624 * @param orientation {@code SwingConstants.HORIZONTAL} or
2625 * {@code SwingConstants.VERTICAL}
2626 * @param direction less or equal to zero to scroll up/back,
2627 * greater than zero for down/forward
2628 * @return the "block" increment for scrolling in the specified direction;
2629 * always positive
2630 * @see #getScrollableUnitIncrement
2631 * @see Scrollable#getScrollableBlockIncrement
2632 * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
2633 * {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
2634 * {@code SwingConstants.HORIZONTAL}
2635 */
2636 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
2637 checkScrollableParameters(visibleRect, orientation);
2638 if (orientation == SwingConstants.VERTICAL) {
2639 int inc = visibleRect.height;
2640 /* Scroll Down */
2641 if (direction > 0) {
2642 // last cell is the lowest left cell
2643 int last = locationToIndex(new Point(visibleRect.x, visibleRect.y+visibleRect.height-1));
2644 if (last != -1) {
2645 Rectangle lastRect = getCellBounds(last,last);
2646 if (lastRect != null) {
2647 inc = lastRect.y - visibleRect.y;
2648 if ( (inc == 0) && (last < getModel().getSize()-1) ) {
2649 inc = lastRect.height;
2650 }
2651 }
2652 }
2653 }
2654 /* Scroll Up */
2655 else {
2656 int newFirst = locationToIndex(new Point(visibleRect.x, visibleRect.y-visibleRect.height));
2657 int first = getFirstVisibleIndex();
2658 if (newFirst != -1) {
2659 if (first == -1) {
2660 first = locationToIndex(visibleRect.getLocation());
2661 }
2662 Rectangle newFirstRect = getCellBounds(newFirst,newFirst);
2663 Rectangle firstRect = getCellBounds(first,first);
2664 if ((newFirstRect != null) && (firstRect!=null)) {
2665 while ( (newFirstRect.y + visibleRect.height <
2666 firstRect.y + firstRect.height) &&
2667 (newFirstRect.y < firstRect.y) ) {
2668 newFirst++;
2669 newFirstRect = getCellBounds(newFirst,newFirst);
2670 }
2671 inc = visibleRect.y - newFirstRect.y;
2672 if ( (inc <= 0) && (newFirstRect.y > 0)) {
2673 newFirst--;
2674 newFirstRect = getCellBounds(newFirst,newFirst);
2675 if (newFirstRect != null) {
2676 inc = visibleRect.y - newFirstRect.y;
2677 }
2678 }
2679 }
2680 }
2681 }
2682 return inc;
2683 }
2684 else if (orientation == SwingConstants.HORIZONTAL &&
2685 getLayoutOrientation() != JList.VERTICAL) {
2686 boolean leftToRight = getComponentOrientation().isLeftToRight();
2687 int inc = visibleRect.width;
2688 /* Scroll Right (in ltr mode) or Scroll Left (in rtl mode) */
2689 if (direction > 0) {
2690 // position is upper right if ltr, or upper left otherwise
2691 int x = visibleRect.x + (leftToRight ? (visibleRect.width - 1) : 0);
2692 int last = locationToIndex(new Point(x, visibleRect.y));
2693
2694 if (last != -1) {
2695 Rectangle lastRect = getCellBounds(last,last);
2696 if (lastRect != null) {
2697 if (leftToRight) {
2698 inc = lastRect.x - visibleRect.x;
2699 } else {
2700 inc = visibleRect.x + visibleRect.width
2701 - (lastRect.x + lastRect.width);
2702 }
2703 if (inc < 0) {
2704 inc += lastRect.width;
2705 } else if ( (inc == 0) && (last < getModel().getSize()-1) ) {
2706 inc = lastRect.width;
2707 }
2708 }
2709 }
2710 }
2711 /* Scroll Left (in ltr mode) or Scroll Right (in rtl mode) */
2712 else {
2713 // position is upper left corner of the visibleRect shifted
2714 // left by the visibleRect.width if ltr, or upper right shifted
2715 // right by the visibleRect.width otherwise
2716 int x = visibleRect.x + (leftToRight
2717 ? -visibleRect.width
2718 : visibleRect.width - 1 + visibleRect.width);
2719 int first = locationToIndex(new Point(x, visibleRect.y));
2720
2721 if (first != -1) {
2722 Rectangle firstRect = getCellBounds(first,first);
2723 if (firstRect != null) {
2724 // the right of the first cell
2725 int firstRight = firstRect.x + firstRect.width;
2726
2727 if (leftToRight) {
2728 if ((firstRect.x < visibleRect.x - visibleRect.width)
2729 && (firstRight < visibleRect.x)) {
2730 inc = visibleRect.x - firstRight;
2731 } else {
2732 inc = visibleRect.x - firstRect.x;
2733 }
2734 } else {
2735 int visibleRight = visibleRect.x + visibleRect.width;
2736
2737 if ((firstRight > visibleRight + visibleRect.width)
2738 && (firstRect.x > visibleRight)) {
2739 inc = firstRect.x - visibleRight;
2740 } else {
2741 inc = firstRight - visibleRight;
2742 }
2743 }
2744 }
2745 }
2746 }
2747 return inc;
2748 }
2749 return visibleRect.width;
2750 }
2751
2752
2753 /**
2754 * Returns {@code true} if this {@code JList} is displayed in a
2755 * {@code JViewport} and the viewport is wider than the list's
2756 * preferred width, or if the layout orientation is {@code HORIZONTAL_WRAP}
2757 * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
2758 * <p>
2759 * If {@code false}, then don't track the viewport's width. This allows
2760 * horizontal scrolling if the {@code JViewport} is itself embedded in a
2761 * {@code JScrollPane}.
2762 *
2763 * @return whether or not an enclosing viewport should force the list's
2764 * width to match its own
2765 * @see Scrollable#getScrollableTracksViewportWidth
2766 */
2767 public boolean getScrollableTracksViewportWidth() {
2768 if (getLayoutOrientation() == HORIZONTAL_WRAP &&
2769 getVisibleRowCount() <= 0) {
2770 return true;
2771 }
2772 Container parent = SwingUtilities.getUnwrappedParent(this);
2773 if (parent instanceof JViewport) {
2774 return parent.getWidth() > getPreferredSize().width;
2775 }
2776 return false;
2777 }
2778
2779 /**
2780 * Returns {@code true} if this {@code JList} is displayed in a
2781 * {@code JViewport} and the viewport is taller than the list's
2782 * preferred height, or if the layout orientation is {@code VERTICAL_WRAP}
2783 * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
2784 * <p>
2785 * If {@code false}, then don't track the viewport's height. This allows
2786 * vertical scrolling if the {@code JViewport} is itself embedded in a
2787 * {@code JScrollPane}.
2788 *
2789 * @return whether or not an enclosing viewport should force the list's
2790 * height to match its own
2791 * @see Scrollable#getScrollableTracksViewportHeight
2792 */
2793 public boolean getScrollableTracksViewportHeight() {
2794 if (getLayoutOrientation() == VERTICAL_WRAP &&
2795 getVisibleRowCount() <= 0) {
2796 return true;
2797 }
2798 Container parent = SwingUtilities.getUnwrappedParent(this);
2799 if (parent instanceof JViewport) {
2800 return parent.getHeight() > getPreferredSize().height;
2801 }
2802 return false;
2803 }
2804
2805
2806 /*
2807 * See {@code readObject} and {@code writeObject} in {@code JComponent}
2808 * for more information about serialization in Swing.
2809 */
2810 private void writeObject(ObjectOutputStream s) throws IOException {
2811 s.defaultWriteObject();
2812 if (getUIClassID().equals(uiClassID)) {
2813 byte count = JComponent.getWriteObjCounter(this);
2814 JComponent.setWriteObjCounter(this, --count);
2815 if (count == 0 && ui != null) {
2816 ui.installUI(this);
2817 }
2818 }
2819 }
2820
2821
2822 /**
2823 * Returns a {@code String} representation of this {@code JList}.
2824 * This method is intended to be used only for debugging purposes,
2825 * and the content and format of the returned {@code String} may vary
2826 * between implementations. The returned {@code String} may be empty,
2827 * but may not be {@code null}.
2828 *
2829 * @return a {@code String} representation of this {@code JList}.
2830 */
2831 protected String paramString() {
2832 String selectionForegroundString = (selectionForeground != null ?
2833 selectionForeground.toString() :
2834 "");
2835 String selectionBackgroundString = (selectionBackground != null ?
2836 selectionBackground.toString() :
2837 "");
2838
2839 return super.paramString() +
2840 ",fixedCellHeight=" + fixedCellHeight +
2841 ",fixedCellWidth=" + fixedCellWidth +
2842 ",horizontalScrollIncrement=" + horizontalScrollIncrement +
2843 ",selectionBackground=" + selectionBackgroundString +
2844 ",selectionForeground=" + selectionForegroundString +
2845 ",visibleRowCount=" + visibleRowCount +
2846 ",layoutOrientation=" + layoutOrientation;
2847 }
2848
2849
2850 /**
2851 * --- Accessibility Support ---
2852 */
2853
2854 /**
2855 * Gets the {@code AccessibleContext} associated with this {@code JList}.
2856 * For {@code JList}, the {@code AccessibleContext} takes the form of an
2857 * {@code AccessibleJList}.
2858 * <p>
2859 * A new {@code AccessibleJList} instance is created if necessary.
2860 *
2861 * @return an {@code AccessibleJList} that serves as the
2862 * {@code AccessibleContext} of this {@code JList}
2863 */
2864 public AccessibleContext getAccessibleContext() {
2865 if (accessibleContext == null) {
2866 accessibleContext = new AccessibleJList();
2867 }
2868 return accessibleContext;
2869 }
2870
2871 /**
2872 * This class implements accessibility support for the
2873 * {@code JList} class. It provides an implementation of the
2874 * Java Accessibility API appropriate to list user-interface
2875 * elements.
2876 * <p>
2877 * <strong>Warning:</strong>
2878 * Serialized objects of this class will not be compatible with
2879 * future Swing releases. The current serialization support is
2880 * appropriate for short term storage or RMI between applications running
2881 * the same version of Swing. As of 1.4, support for long term storage
2882 * of all JavaBeans<sup><font size="-2">TM</font></sup>
2883 * has been added to the <code>java.beans</code> package.
2884 * Please see {@link java.beans.XMLEncoder}.
2885 */
2886 protected class AccessibleJList extends AccessibleJComponent
2887 implements AccessibleSelection, PropertyChangeListener,
2888 ListSelectionListener, ListDataListener {
2889
2890 int leadSelectionIndex;
2891
2892 public AccessibleJList() {
2893 super();
2894 JList.this.addPropertyChangeListener(this);
2895 JList.this.getSelectionModel().addListSelectionListener(this);
2896 JList.this.getModel().addListDataListener(this);
2897 leadSelectionIndex = JList.this.getLeadSelectionIndex();
2898 }
2899
2900 /**
2901 * Property Change Listener change method. Used to track changes
2902 * to the DataModel and ListSelectionModel, in order to re-set
2903 * listeners to those for reporting changes there via the Accessibility
2904 * PropertyChange mechanism.
2905 *
2906 * @param e PropertyChangeEvent
2907 */
2908 public void propertyChange(PropertyChangeEvent e) {
2909 String name = e.getPropertyName();
2910 Object oldValue = e.getOldValue();
2911 Object newValue = e.getNewValue();
2912
2913 // re-set listData listeners
2914 if (name.compareTo("model") == 0) {
2915
2916 if (oldValue != null && oldValue instanceof ListModel) {
2917 ((ListModel) oldValue).removeListDataListener(this);
2918 }
2919 if (newValue != null && newValue instanceof ListModel) {
2920 ((ListModel) newValue).addListDataListener(this);
2921 }
2922
2923 // re-set listSelectionModel listeners
2924 } else if (name.compareTo("selectionModel") == 0) {
2925
2926 if (oldValue != null && oldValue instanceof ListSelectionModel) {
2927 ((ListSelectionModel) oldValue).removeListSelectionListener(this);
2928 }
2929 if (newValue != null && newValue instanceof ListSelectionModel) {
2930 ((ListSelectionModel) newValue).addListSelectionListener(this);
2931 }
2932
2933 firePropertyChange(
2934 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2935 Boolean.valueOf(false), Boolean.valueOf(true));
2936 }
2937 }
2938
2939 /**
2940 * List Selection Listener value change method. Used to fire
2941 * the property change
2942 *
2943 * @param e ListSelectionEvent
2944 *
2945 */
2946 public void valueChanged(ListSelectionEvent e) {
2947 int oldLeadSelectionIndex = leadSelectionIndex;
2948 leadSelectionIndex = JList.this.getLeadSelectionIndex();
2949 if (oldLeadSelectionIndex != leadSelectionIndex) {
2950 Accessible oldLS, newLS;
2951 oldLS = (oldLeadSelectionIndex >= 0)
2952 ? getAccessibleChild(oldLeadSelectionIndex)
2953 : null;
2954 newLS = (leadSelectionIndex >= 0)
2955 ? getAccessibleChild(leadSelectionIndex)
2956 : null;
2957 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
2958 oldLS, newLS);
2959 }
2960
2961 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2962 Boolean.valueOf(false), Boolean.valueOf(true));
2963 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2964 Boolean.valueOf(false), Boolean.valueOf(true));
2965
2966 // Process the State changes for Multiselectable
2967 AccessibleStateSet s = getAccessibleStateSet();
2968 ListSelectionModel lsm = JList.this.getSelectionModel();
2969 if (lsm.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) {
2970 if (!s.contains(AccessibleState.MULTISELECTABLE)) {
2971 s.add(AccessibleState.MULTISELECTABLE);
2972 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2973 null, AccessibleState.MULTISELECTABLE);
2974 }
2975 } else {
2976 if (s.contains(AccessibleState.MULTISELECTABLE)) {
2977 s.remove(AccessibleState.MULTISELECTABLE);
2978 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2979 AccessibleState.MULTISELECTABLE, null);
2980 }
2981 }
2982 }
2983
2984 /**
2985 * List Data Listener interval added method. Used to fire the visible data property change
2986 *
2987 * @param e ListDataEvent
2988 *
2989 */
2990 public void intervalAdded(ListDataEvent e) {
2991 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2992 Boolean.valueOf(false), Boolean.valueOf(true));
2993 }
2994
2995 /**
2996 * List Data Listener interval removed method. Used to fire the visible data property change
2997 *
2998 * @param e ListDataEvent
2999 *
3000 */
3001 public void intervalRemoved(ListDataEvent e) {
3002 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
3003 Boolean.valueOf(false), Boolean.valueOf(true));
3004 }
3005
3006 /**
3007 * List Data Listener contents changed method. Used to fire the visible data property change
3008 *
3009 * @param e ListDataEvent
3010 *
3011 */
3012 public void contentsChanged(ListDataEvent e) {
3013 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
3014 Boolean.valueOf(false), Boolean.valueOf(true));
3015 }
3016
3017 // AccessibleContext methods
3018
3019 /**
3020 * Get the state set of this object.
3021 *
3022 * @return an instance of AccessibleState containing the current state
3023 * of the object
3024 * @see AccessibleState
3025 */
3026 public AccessibleStateSet getAccessibleStateSet() {
3027 AccessibleStateSet states = super.getAccessibleStateSet();
3028 if (selectionModel.getSelectionMode() !=
3029 ListSelectionModel.SINGLE_SELECTION) {
3030 states.add(AccessibleState.MULTISELECTABLE);
3031 }
3032 return states;
3033 }
3034
3035 /**
3036 * Get the role of this object.
3037 *
3038 * @return an instance of AccessibleRole describing the role of the
3039 * object
3040 * @see AccessibleRole
3041 */
3042 public AccessibleRole getAccessibleRole() {
3043 return AccessibleRole.LIST;
3044 }
3045
3046 /**
3047 * Returns the <code>Accessible</code> child contained at
3048 * the local coordinate <code>Point</code>, if one exists.
3049 * Otherwise returns <code>null</code>.
3050 *
3051 * @return the <code>Accessible</code> at the specified
3052 * location, if it exists
3053 */
3054 public Accessible getAccessibleAt(Point p) {
3055 int i = locationToIndex(p);
3056 if (i >= 0) {
3057 return new AccessibleJListChild(JList.this, i);
3058 } else {
3059 return null;
3060 }
3061 }
3062
3063 /**
3064 * Returns the number of accessible children in the object. If all
3065 * of the children of this object implement Accessible, than this
3066 * method should return the number of children of this object.
3067 *
3068 * @return the number of accessible children in the object.
3069 */
3070 public int getAccessibleChildrenCount() {
3071 return getModel().getSize();
3072 }
3073
3074 /**
3075 * Return the nth Accessible child of the object.
3076 *
3077 * @param i zero-based index of child
3078 * @return the nth Accessible child of the object
3079 */
3080 public Accessible getAccessibleChild(int i) {
3081 if (i >= getModel().getSize()) {
3082 return null;
3083 } else {
3084 return new AccessibleJListChild(JList.this, i);
3085 }
3086 }
3087
3088 /**
3089 * Get the AccessibleSelection associated with this object. In the
3090 * implementation of the Java Accessibility API for this class,
3091 * return this object, which is responsible for implementing the
3092 * AccessibleSelection interface on behalf of itself.
3093 *
3094 * @return this object
3095 */
3096 public AccessibleSelection getAccessibleSelection() {
3097 return this;
3098 }
3099
3100
3101 // AccessibleSelection methods
3102
3103 /**
3104 * Returns the number of items currently selected.
3105 * If no items are selected, the return value will be 0.
3106 *
3107 * @return the number of items currently selected.
3108 */
3109 public int getAccessibleSelectionCount() {
3110 return JList.this.getSelectedIndices().length;
3111 }
3112
3113 /**
3114 * Returns an Accessible representing the specified selected item
3115 * in the object. If there isn't a selection, or there are
3116 * fewer items selected than the integer passed in, the return
3117 * value will be <code>null</code>.
3118 *
3119 * @param i the zero-based index of selected items
3120 * @return an Accessible containing the selected item
3121 */
3122 public Accessible getAccessibleSelection(int i) {
3123 int len = getAccessibleSelectionCount();
3124 if (i < 0 || i >= len) {
3125 return null;
3126 } else {
3127 return getAccessibleChild(JList.this.getSelectedIndices()[i]);
3128 }
3129 }
3130
3131 /**
3132 * Returns true if the current child of this object is selected.
3133 *
3134 * @param i the zero-based index of the child in this Accessible
3135 * object.
3136 * @see AccessibleContext#getAccessibleChild
3137 */
3138 public boolean isAccessibleChildSelected(int i) {
3139 return isSelectedIndex(i);
3140 }
3141
3142 /**
3143 * Adds the specified selected item in the object to the object's
3144 * selection. If the object supports multiple selections,
3145 * the specified item is added to any existing selection, otherwise
3146 * it replaces any existing selection in the object. If the
3147 * specified item is already selected, this method has no effect.
3148 *
3149 * @param i the zero-based index of selectable items
3150 */
3151 public void addAccessibleSelection(int i) {
3152 JList.this.addSelectionInterval(i, i);
3153 }
3154
3155 /**
3156 * Removes the specified selected item in the object from the object's
3157 * selection. If the specified item isn't currently selected, this
3158 * method has no effect.
3159 *
3160 * @param i the zero-based index of selectable items
3161 */
3162 public void removeAccessibleSelection(int i) {
3163 JList.this.removeSelectionInterval(i, i);
3164 }
3165
3166 /**
3167 * Clears the selection in the object, so that nothing in the
3168 * object is selected.
3169 */
3170 public void clearAccessibleSelection() {
3171 JList.this.clearSelection();
3172 }
3173
3174 /**
3175 * Causes every selected item in the object to be selected
3176 * if the object supports multiple selections.
3177 */
3178 public void selectAllAccessibleSelection() {
3179 JList.this.addSelectionInterval(0, getAccessibleChildrenCount() -1);
3180 }
3181
3182 /**
3183 * This class implements accessibility support appropriate
3184 * for list children.
3185 */
3186 protected class AccessibleJListChild extends AccessibleContext
3187 implements Accessible, AccessibleComponent {
3188 private JList<E> parent = null;
3189 private int indexInParent;
3190 private Component component = null;
3191 private AccessibleContext accessibleContext = null;
3192 private ListModel<E> listModel;
3193 private ListCellRenderer<? super E> cellRenderer = null;
3194
3195 public AccessibleJListChild(JList<E> parent, int indexInParent) {
3196 this.parent = parent;
3197 this.setAccessibleParent(parent);
3198 this.indexInParent = indexInParent;
3199 if (parent != null) {
3200 listModel = parent.getModel();
3201 cellRenderer = parent.getCellRenderer();
3202 }
3203 }
3204
3205 private Component getCurrentComponent() {
3206 return getComponentAtIndex(indexInParent);
3207 }
3208
3209 private AccessibleContext getCurrentAccessibleContext() {
3210 Component c = getComponentAtIndex(indexInParent);
3211 if (c instanceof Accessible) {
3212 return c.getAccessibleContext();
3213 } else {
3214 return null;
3215 }
3216 }
3217
3218 private Component getComponentAtIndex(int index) {
3219 if (index < 0 || index >= listModel.getSize()) {
3220 return null;
3221 }
3222 if ((parent != null)
3223 && (listModel != null)
3224 && cellRenderer != null) {
3225 E value = listModel.getElementAt(index);
3226 boolean isSelected = parent.isSelectedIndex(index);
3227 boolean isFocussed = parent.isFocusOwner()
3228 && (index == parent.getLeadSelectionIndex());
3229 return cellRenderer.getListCellRendererComponent(
3230 parent,
3231 value,
3232 index,
3233 isSelected,
3234 isFocussed);
3235 } else {
3236 return null;
3237 }
3238 }
3239
3240
3241 // Accessible Methods
3242 /**
3243 * Get the AccessibleContext for this object. In the
3244 * implementation of the Java Accessibility API for this class,
3245 * returns this object, which is its own AccessibleContext.
3246 *
3247 * @return this object
3248 */
3249 public AccessibleContext getAccessibleContext() {
3250 return this;
3251 }
3252
3253
3254 // AccessibleContext methods
3255
3256 public String getAccessibleName() {
3257 AccessibleContext ac = getCurrentAccessibleContext();
3258 if (ac != null) {
3259 return ac.getAccessibleName();
3260 } else {
3261 return null;
3262 }
3263 }
3264
3265 public void setAccessibleName(String s) {
3266 AccessibleContext ac = getCurrentAccessibleContext();
3267 if (ac != null) {
3268 ac.setAccessibleName(s);
3269 }
3270 }
3271
3272 public String getAccessibleDescription() {
3273 AccessibleContext ac = getCurrentAccessibleContext();
3274 if (ac != null) {
3275 return ac.getAccessibleDescription();
3276 } else {
3277 return null;
3278 }
3279 }
3280
3281 public void setAccessibleDescription(String s) {
3282 AccessibleContext ac = getCurrentAccessibleContext();
3283 if (ac != null) {
3284 ac.setAccessibleDescription(s);
3285 }
3286 }
3287
3288 public AccessibleRole getAccessibleRole() {
3289 AccessibleContext ac = getCurrentAccessibleContext();
3290 if (ac != null) {
3291 return ac.getAccessibleRole();
3292 } else {
3293 return null;
3294 }
3295 }
3296
3297 public AccessibleStateSet getAccessibleStateSet() {
3298 AccessibleContext ac = getCurrentAccessibleContext();
3299 AccessibleStateSet s;
3300 if (ac != null) {
3301 s = ac.getAccessibleStateSet();
3302 } else {
3303 s = new AccessibleStateSet();
3304 }
3305
3306 s.add(AccessibleState.SELECTABLE);
3307 if (parent.isFocusOwner()
3308 && (indexInParent == parent.getLeadSelectionIndex())) {
3309 s.add(AccessibleState.ACTIVE);
3310 }
3311 if (parent.isSelectedIndex(indexInParent)) {
3312 s.add(AccessibleState.SELECTED);
3313 }
3314 if (this.isShowing()) {
3315 s.add(AccessibleState.SHOWING);
3316 } else if (s.contains(AccessibleState.SHOWING)) {
3317 s.remove(AccessibleState.SHOWING);
3318 }
3319 if (this.isVisible()) {
3320 s.add(AccessibleState.VISIBLE);
3321 } else if (s.contains(AccessibleState.VISIBLE)) {
3322 s.remove(AccessibleState.VISIBLE);
3323 }
3324 s.add(AccessibleState.TRANSIENT); // cell-rendered
3325 return s;
3326 }
3327
3328 public int getAccessibleIndexInParent() {
3329 return indexInParent;
3330 }
3331
3332 public int getAccessibleChildrenCount() {
3333 AccessibleContext ac = getCurrentAccessibleContext();
3334 if (ac != null) {
3335 return ac.getAccessibleChildrenCount();
3336 } else {
3337 return 0;
3338 }
3339 }
3340
3341 public Accessible getAccessibleChild(int i) {
3342 AccessibleContext ac = getCurrentAccessibleContext();
3343 if (ac != null) {
3344 Accessible accessibleChild = ac.getAccessibleChild(i);
3345 ac.setAccessibleParent(this);
3346 return accessibleChild;
3347 } else {
3348 return null;
3349 }
3350 }
3351
3352 public Locale getLocale() {
3353 AccessibleContext ac = getCurrentAccessibleContext();
3354 if (ac != null) {
3355 return ac.getLocale();
3356 } else {
3357 return null;
3358 }
3359 }
3360
3361 public void addPropertyChangeListener(PropertyChangeListener l) {
3362 AccessibleContext ac = getCurrentAccessibleContext();
3363 if (ac != null) {
3364 ac.addPropertyChangeListener(l);
3365 }
3366 }
3367
3368 public void removePropertyChangeListener(PropertyChangeListener l) {
3369 AccessibleContext ac = getCurrentAccessibleContext();
3370 if (ac != null) {
3371 ac.removePropertyChangeListener(l);
3372 }
3373 }
3374
3375 public AccessibleAction getAccessibleAction() {
3376 return getCurrentAccessibleContext().getAccessibleAction();
3377 }
3378
3379 /**
3380 * Get the AccessibleComponent associated with this object. In the
3381 * implementation of the Java Accessibility API for this class,
3382 * return this object, which is responsible for implementing the
3383 * AccessibleComponent interface on behalf of itself.
3384 *
3385 * @return this object
3386 */
3387 public AccessibleComponent getAccessibleComponent() {
3388 return this; // to override getBounds()
3389 }
3390
3391 public AccessibleSelection getAccessibleSelection() {
3392 return getCurrentAccessibleContext().getAccessibleSelection();
3393 }
3394
3395 public AccessibleText getAccessibleText() {
3396 return getCurrentAccessibleContext().getAccessibleText();
3397 }
3398
3399 public AccessibleValue getAccessibleValue() {
3400 return getCurrentAccessibleContext().getAccessibleValue();
3401 }
3402
3403
3404 // AccessibleComponent methods
3405
3406 public Color getBackground() {
3407 AccessibleContext ac = getCurrentAccessibleContext();
3408 if (ac instanceof AccessibleComponent) {
3409 return ((AccessibleComponent) ac).getBackground();
3410 } else {
3411 Component c = getCurrentComponent();
3412 if (c != null) {
3413 return c.getBackground();
3414 } else {
3415 return null;
3416 }
3417 }
3418 }
3419
3420 public void setBackground(Color c) {
3421 AccessibleContext ac = getCurrentAccessibleContext();
3422 if (ac instanceof AccessibleComponent) {
3423 ((AccessibleComponent) ac).setBackground(c);
3424 } else {
3425 Component cp = getCurrentComponent();
3426 if (cp != null) {
3427 cp.setBackground(c);
3428 }
3429 }
3430 }
3431
3432 public Color getForeground() {
3433 AccessibleContext ac = getCurrentAccessibleContext();
3434 if (ac instanceof AccessibleComponent) {
3435 return ((AccessibleComponent) ac).getForeground();
3436 } else {
3437 Component c = getCurrentComponent();
3438 if (c != null) {
3439 return c.getForeground();
3440 } else {
3441 return null;
3442 }
3443 }
3444 }
3445
3446 public void setForeground(Color c) {
3447 AccessibleContext ac = getCurrentAccessibleContext();
3448 if (ac instanceof AccessibleComponent) {
3449 ((AccessibleComponent) ac).setForeground(c);
3450 } else {
3451 Component cp = getCurrentComponent();
3452 if (cp != null) {
3453 cp.setForeground(c);
3454 }
3455 }
3456 }
3457
3458 public Cursor getCursor() {
3459 AccessibleContext ac = getCurrentAccessibleContext();
3460 if (ac instanceof AccessibleComponent) {
3461 return ((AccessibleComponent) ac).getCursor();
3462 } else {
3463 Component c = getCurrentComponent();
3464 if (c != null) {
3465 return c.getCursor();
3466 } else {
3467 Accessible ap = getAccessibleParent();
3468 if (ap instanceof AccessibleComponent) {
3469 return ((AccessibleComponent) ap).getCursor();
3470 } else {
3471 return null;
3472 }
3473 }
3474 }
3475 }
3476
3477 public void setCursor(Cursor c) {
3478 AccessibleContext ac = getCurrentAccessibleContext();
3479 if (ac instanceof AccessibleComponent) {
3480 ((AccessibleComponent) ac).setCursor(c);
3481 } else {
3482 Component cp = getCurrentComponent();
3483 if (cp != null) {
3484 cp.setCursor(c);
3485 }
3486 }
3487 }
3488
3489 public Font getFont() {
3490 AccessibleContext ac = getCurrentAccessibleContext();
3491 if (ac instanceof AccessibleComponent) {
3492 return ((AccessibleComponent) ac).getFont();
3493 } else {
3494 Component c = getCurrentComponent();
3495 if (c != null) {
3496 return c.getFont();
3497 } else {
3498 return null;
3499 }
3500 }
3501 }
3502
3503 public void setFont(Font f) {
3504 AccessibleContext ac = getCurrentAccessibleContext();
3505 if (ac instanceof AccessibleComponent) {
3506 ((AccessibleComponent) ac).setFont(f);
3507 } else {
3508 Component c = getCurrentComponent();
3509 if (c != null) {
3510 c.setFont(f);
3511 }
3512 }
3513 }
3514
3515 public FontMetrics getFontMetrics(Font f) {
3516 AccessibleContext ac = getCurrentAccessibleContext();
3517 if (ac instanceof AccessibleComponent) {
3518 return ((AccessibleComponent) ac).getFontMetrics(f);
3519 } else {
3520 Component c = getCurrentComponent();
3521 if (c != null) {
3522 return c.getFontMetrics(f);
3523 } else {
3524 return null;
3525 }
3526 }
3527 }
3528
3529 public boolean isEnabled() {
3530 AccessibleContext ac = getCurrentAccessibleContext();
3531 if (ac instanceof AccessibleComponent) {
3532 return ((AccessibleComponent) ac).isEnabled();
3533 } else {
3534 Component c = getCurrentComponent();
3535 if (c != null) {
3536 return c.isEnabled();
3537 } else {
3538 return false;
3539 }
3540 }
3541 }
3542
3543 public void setEnabled(boolean b) {
3544 AccessibleContext ac = getCurrentAccessibleContext();
3545 if (ac instanceof AccessibleComponent) {
3546 ((AccessibleComponent) ac).setEnabled(b);
3547 } else {
3548 Component c = getCurrentComponent();
3549 if (c != null) {
3550 c.setEnabled(b);
3551 }
3552 }
3553 }
3554
3555 public boolean isVisible() {
3556 int fi = parent.getFirstVisibleIndex();
3557 int li = parent.getLastVisibleIndex();
3558 // The UI incorrectly returns a -1 for the last
3559 // visible index if the list is smaller than the
3560 // viewport size.
3561 if (li == -1) {
3562 li = parent.getModel().getSize() - 1;
3563 }
3564 return ((indexInParent >= fi)
3565 && (indexInParent <= li));
3566 }
3567
3568 public void setVisible(boolean b) {
3569 }
3570
3571 public boolean isShowing() {
3572 return (parent.isShowing() && isVisible());
3573 }
3574
3575 public boolean contains(Point p) {
3576 AccessibleContext ac = getCurrentAccessibleContext();
3577 if (ac instanceof AccessibleComponent) {
3578 Rectangle r = ((AccessibleComponent) ac).getBounds();
3579 return r.contains(p);
3580 } else {
3581 Component c = getCurrentComponent();
3582 if (c != null) {
3583 Rectangle r = c.getBounds();
3584 return r.contains(p);
3585 } else {
3586 return getBounds().contains(p);
3587 }
3588 }
3589 }
3590
3591 public Point getLocationOnScreen() {
3592 if (parent != null) {
3593 Point listLocation = parent.getLocationOnScreen();
3594 Point componentLocation = parent.indexToLocation(indexInParent);
3595 if (componentLocation != null) {
3596 componentLocation.translate(listLocation.x, listLocation.y);
3597 return componentLocation;
3598 } else {
3599 return null;
3600 }
3601 } else {
3602 return null;
3603 }
3604 }
3605
3606 public Point getLocation() {
3607 if (parent != null) {
3608 return parent.indexToLocation(indexInParent);
3609 } else {
3610 return null;
3611 }
3612 }
3613
3614 public void setLocation(Point p) {
3615 if ((parent != null) && (parent.contains(p))) {
3616 ensureIndexIsVisible(indexInParent);
3617 }
3618 }
3619
3620 public Rectangle getBounds() {
3621 if (parent != null) {
3622 return parent.getCellBounds(indexInParent,indexInParent);
3623 } else {
3624 return null;
3625 }
3626 }
3627
3628 public void setBounds(Rectangle r) {
3629 AccessibleContext ac = getCurrentAccessibleContext();
3630 if (ac instanceof AccessibleComponent) {
3631 ((AccessibleComponent) ac).setBounds(r);
3632 }
3633 }
3634
3635 public Dimension getSize() {
3636 Rectangle cellBounds = this.getBounds();
3637 if (cellBounds != null) {
3638 return cellBounds.getSize();
3639 } else {
3640 return null;
3641 }
3642 }
3643
3644 public void setSize (Dimension d) {
3645 AccessibleContext ac = getCurrentAccessibleContext();
3646 if (ac instanceof AccessibleComponent) {
3647 ((AccessibleComponent) ac).setSize(d);
3648 } else {
3649 Component c = getCurrentComponent();
3650 if (c != null) {
3651 c.setSize(d);
3652 }
3653 }
3654 }
3655
3656 public Accessible getAccessibleAt(Point p) {
3657 AccessibleContext ac = getCurrentAccessibleContext();
3658 if (ac instanceof AccessibleComponent) {
3659 return ((AccessibleComponent) ac).getAccessibleAt(p);
3660 } else {
3661 return null;
3662 }
3663 }
3664
3665 public boolean isFocusTraversable() {
3666 AccessibleContext ac = getCurrentAccessibleContext();
3667 if (ac instanceof AccessibleComponent) {
3668 return ((AccessibleComponent) ac).isFocusTraversable();
3669 } else {
3670 Component c = getCurrentComponent();
3671 if (c != null) {
3672 return c.isFocusTraversable();
3673 } else {
3674 return false;
3675 }
3676 }
3677 }
3678
3679 public void requestFocus() {
3680 AccessibleContext ac = getCurrentAccessibleContext();
3681 if (ac instanceof AccessibleComponent) {
3682 ((AccessibleComponent) ac).requestFocus();
3683 } else {
3684 Component c = getCurrentComponent();
3685 if (c != null) {
3686 c.requestFocus();
3687 }
3688 }
3689 }
3690
3691 public void addFocusListener(FocusListener l) {
3692 AccessibleContext ac = getCurrentAccessibleContext();
3693 if (ac instanceof AccessibleComponent) {
3694 ((AccessibleComponent) ac).addFocusListener(l);
3695 } else {
3696 Component c = getCurrentComponent();
3697 if (c != null) {
3698 c.addFocusListener(l);
3699 }
3700 }
3701 }
3702
3703 public void removeFocusListener(FocusListener l) {
3704 AccessibleContext ac = getCurrentAccessibleContext();
3705 if (ac instanceof AccessibleComponent) {
3706 ((AccessibleComponent) ac).removeFocusListener(l);
3707 } else {
3708 Component c = getCurrentComponent();
3709 if (c != null) {
3710 c.removeFocusListener(l);
3711 }
3712 }
3713 }
3714
3715 // TIGER - 4733624
3716 /**
3717 * Returns the icon for the element renderer, as the only item
3718 * of an array of <code>AccessibleIcon</code>s or a <code>null</code> array
3719 * if the renderer component contains no icons.
3720 *
3721 * @return an array containing the accessible icon
3722 * or a <code>null</code> array if none
3723 * @since 1.3
3724 */
3725 public AccessibleIcon [] getAccessibleIcon() {
3726 AccessibleContext ac = getCurrentAccessibleContext();
3727 if (ac != null) {
3728 return ac.getAccessibleIcon();
3729 } else {
3730 return null;
3731 }
3732 }
3733 } // inner class AccessibleJListChild
3734 } // inner class AccessibleJList
3735 }